1 /*******************************************************************************
2 * Copyright (c) 2000, 2019 IBM Corporation and others.
3 *
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
8 *
9 * SPDX-License-Identifier: EPL-2.0
10 *
11 * Contributors:
12 * IBM Corporation - initial API and implementation
13 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 483540
14 *******************************************************************************/
15 package org.eclipse.swt.widgets;
16
17
18 import org.eclipse.swt.*;
19 import org.eclipse.swt.events.*;
20 import org.eclipse.swt.graphics.*;
21 import org.eclipse.swt.internal.*;
22 import org.eclipse.swt.internal.win32.*;
23
24 /**
25 * Instances of this class are controls that allow the user
26 * to choose an item from a list of items, or optionally
27 * enter a new value by typing it into an editable text
28 * field. Often, <code>Combo</code>s are used in the same place
29 * where a single selection <code>List</code> widget could
30 * be used but space is limited. A <code>Combo</code> takes
31 * less space than a <code>List</code> widget and shows
32 * similar information.
33 * <p>
34 * Note: Since <code>Combo</code>s can contain both a list
35 * and an editable text field, it is possible to confuse methods
36 * which access one versus the other (compare for example,
37 * <code>clearSelection()</code> and <code>deselectAll()</code>).
38 * The API documentation is careful to indicate either "the
39 * receiver's list" or the "the receiver's text field" to
40 * distinguish between the two cases.
41 * </p><p>
42 * Note that although this class is a subclass of <code>Composite</code>,
43 * it does not make sense to add children to it, or set a layout on it.
44 * </p>
45 * <dl>
46 * <dt><b>Styles:</b></dt>
47 * <dd>DROP_DOWN, READ_ONLY, SIMPLE</dd>
48 * <dt><b>Events:</b></dt>
49 * <dd>DefaultSelection, Modify, Selection, Verify, OrientationChange</dd>
50 * </dl>
51 * <p>
52 * Note: Only one of the styles DROP_DOWN and SIMPLE may be specified.
53 * </p><p>
54 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
55 * </p>
56 *
57 * @see List
58 * @see <a href="http://www.eclipse.org/swt/snippets/#combo">Combo snippets</a>
59 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a>
60 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
61 * @noextend This class is not intended to be subclassed by clients.
62 */
63 public class Combo extends Composite {
64 boolean noSelection, ignoreDefaultSelection, ignoreCharacter, ignoreModify, ignoreResize, lockText;
65 int scrollWidth, visibleCount;
66 long cbtHook;
67 String [] items = new String [0];
68 int[] segments;
69 int clearSegmentsCount = 0;
70 boolean stateFlagsUsable;
71
72 static final char LTR_MARK = '\u200e';
73 static final char RTL_MARK = '\u200f';
74 static final int VISIBLE_COUNT = 5;
75
76 /**
77 * the operating system limit for the number of characters
78 * that the text field in an instance of this class can hold
79 */
80 public static final int LIMIT;
81
82 /*
83 * These values can be different on different platforms.
84 * Therefore they are not initialized in the declaration
85 * to stop the compiler from inlining.
86 */
87 static {
88 LIMIT = 0x7FFFFFFF;
89 }
90
91 /*
92 * These are the undocumented control id's for the children of
93 * a combo box. Since there are no constants for these values,
94 * they may change with different versions of Windows (but have
95 * been the same since Windows 3.0).
96 */
97 static final int CBID_LIST = 1000;
98 static final int CBID_EDIT = 1001;
99 static /*final*/ long EditProc, ListProc;
100
101 static final long ComboProc;
102 static final TCHAR ComboClass = new TCHAR (0, "COMBOBOX", true);
103 static {
104 WNDCLASS lpWndClass = new WNDCLASS ();
105 OS.GetClassInfo (0, ComboClass, lpWndClass);
106 ComboProc = lpWndClass.lpfnWndProc;
107 }
108
109 /* Undocumented values. Remained the same at least between Win7 and Win10 */
110 static final int stateFlagsOffset = (C.PTR_SIZEOF == 8) ? 0x68 : 0x54;
111 static final int stateFlagsFirstPaint = 0x02000000;
112
113 /**
114 * Constructs a new instance of this class given its parent
115 * and a style value describing its behavior and appearance.
116 * <p>
117 * The style value is either one of the style constants defined in
118 * class <code>SWT</code> which is applicable to instances of this
119 * class, or must be built by <em>bitwise OR</em>'ing together
120 * (that is, using the <code>int</code> "|" operator) two or more
121 * of those <code>SWT</code> style constants. The class description
122 * lists the style constants that are applicable to the class.
123 * Style bits are also inherited from superclasses.
124 * </p>
125 *
126 * @param parent a composite control which will be the parent of the new instance (cannot be null)
127 * @param style the style of control to construct
128 *
129 * @exception IllegalArgumentException <ul>
130 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
131 * </ul>
132 * @exception SWTException <ul>
133 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
134 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
135 * </ul>
136 *
137 * @see SWT#DROP_DOWN
138 * @see SWT#READ_ONLY
139 * @see SWT#SIMPLE
140 * @see Widget#checkSubclass
141 * @see Widget#getStyle
142 */
Combo(Composite parent, int style)143 public Combo (Composite parent, int style) {
144 super (parent, checkStyle (style));
145 this.style |= SWT.H_SCROLL;
146 }
147
148 /**
149 * Adds the argument to the end of the receiver's list.
150 * <p>
151 * Note: If control characters like '\n', '\t' etc. are used
152 * in the string, then the behavior is platform dependent.
153 * </p>
154 * @param string the new item
155 *
156 * @exception IllegalArgumentException <ul>
157 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
158 * </ul>
159 * @exception SWTException <ul>
160 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
161 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
162 * </ul>
163 *
164 * @see #add(String,int)
165 */
add(String string)166 public void add (String string) {
167 checkWidget ();
168 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
169 TCHAR buffer = new TCHAR (getCodePage (), string, true);
170 int result = (int)OS.SendMessage (handle, OS.CB_ADDSTRING, 0, buffer);
171 if (result == OS.CB_ERR) error (SWT.ERROR_ITEM_NOT_ADDED);
172 if (result == OS.CB_ERRSPACE) error (SWT.ERROR_ITEM_NOT_ADDED);
173 if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer.chars, true);
174 }
175
176 /**
177 * Adds the argument to the receiver's list at the given
178 * zero-relative index.
179 * <p>
180 * Note: To add an item at the end of the list, use the
181 * result of calling <code>getItemCount()</code> as the
182 * index or use <code>add(String)</code>.
183 * </p><p>
184 * Also note, if control characters like '\n', '\t' etc. are used
185 * in the string, then the behavior is platform dependent.
186 * </p>
187 *
188 * @param string the new item
189 * @param index the index for the item
190 *
191 * @exception IllegalArgumentException <ul>
192 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
193 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li>
194 * </ul>
195 * @exception SWTException <ul>
196 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
197 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
198 * </ul>
199 *
200 * @see #add(String)
201 */
add(String string, int index)202 public void add (String string, int index) {
203 checkWidget ();
204 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
205 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
206 if (!(0 <= index && index <= count)) {
207 error (SWT.ERROR_INVALID_RANGE);
208 }
209 TCHAR buffer = new TCHAR (getCodePage (), string, true);
210 int result = (int)OS.SendMessage (handle, OS.CB_INSERTSTRING, index, buffer);
211 if (result == OS.CB_ERRSPACE || result == OS.CB_ERR) {
212 error (SWT.ERROR_ITEM_NOT_ADDED);
213 }
214 if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer.chars, true);
215 }
216
217 /**
218 * Adds the listener to the collection of listeners who will
219 * be notified when the receiver's text is modified, by sending
220 * it one of the messages defined in the <code>ModifyListener</code>
221 * interface.
222 *
223 * @param listener the listener which should be notified
224 *
225 * @exception IllegalArgumentException <ul>
226 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
227 * </ul>
228 * @exception SWTException <ul>
229 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
230 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
231 * </ul>
232 *
233 * @see ModifyListener
234 * @see #removeModifyListener
235 */
addModifyListener(ModifyListener listener)236 public void addModifyListener (ModifyListener listener) {
237 checkWidget ();
238 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
239 TypedListener typedListener = new TypedListener (listener);
240 addListener (SWT.Modify, typedListener);
241 }
242
243 /**
244 * Adds a segment listener.
245 * <p>
246 * A <code>SegmentEvent</code> is sent whenever text content is being modified or
247 * a segment listener is added or removed. You can
248 * customize the appearance of text by indicating certain characters to be inserted
249 * at certain text offsets. This may be used for bidi purposes, e.g. when
250 * adjacent segments of right-to-left text should not be reordered relative to
251 * each other.
252 * E.g., multiple Java string literals in a right-to-left language
253 * should generally remain in logical order to each other, that is, the
254 * way they are stored.
255 * </p>
256 * <p>
257 * <b>Warning</b>: This API is currently only implemented on Windows.
258 * <code>SegmentEvent</code>s won't be sent on GTK and Cocoa.
259 * </p>
260 *
261 * @param listener the listener which should be notified
262 *
263 * @exception IllegalArgumentException <ul>
264 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
265 * </ul>
266 * @exception SWTException <ul>
267 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
268 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
269 * </ul>
270 *
271 * @see SegmentEvent
272 * @see SegmentListener
273 * @see #removeSegmentListener
274 *
275 * @since 3.103
276 */
addSegmentListener(SegmentListener listener)277 public void addSegmentListener (SegmentListener listener) {
278 checkWidget ();
279 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
280 addListener (SWT.Segments, new TypedListener (listener));
281 int selection = OS.CB_ERR;
282 if (!noSelection) {
283 selection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
284 }
285 clearSegments (true);
286 applyEditSegments ();
287 applyListSegments ();
288 if (selection != OS.CB_ERR) {
289 OS.SendMessage (handle, OS.CB_SETCURSEL, selection, 0);
290 }
291 }
292
293 /**
294 * Adds the listener to the collection of listeners who will
295 * be notified when the user changes the receiver's selection, by sending
296 * it one of the messages defined in the <code>SelectionListener</code>
297 * interface.
298 * <p>
299 * <code>widgetSelected</code> is called when the user changes the combo's list selection.
300 * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed the combo's text area.
301 * </p>
302 *
303 * @param listener the listener which should be notified
304 *
305 * @exception IllegalArgumentException <ul>
306 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
307 * </ul>
308 * @exception SWTException <ul>
309 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
310 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
311 * </ul>
312 *
313 * @see SelectionListener
314 * @see #removeSelectionListener
315 * @see SelectionEvent
316 */
addSelectionListener(SelectionListener listener)317 public void addSelectionListener(SelectionListener listener) {
318 checkWidget ();
319 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
320 TypedListener typedListener = new TypedListener (listener);
321 addListener (SWT.Selection,typedListener);
322 addListener (SWT.DefaultSelection,typedListener);
323 }
324
325 /**
326 * Adds the listener to the collection of listeners who will
327 * be notified when the receiver's text is verified, by sending
328 * it one of the messages defined in the <code>VerifyListener</code>
329 * interface.
330 *
331 * @param listener the listener which should be notified
332 *
333 * @exception IllegalArgumentException <ul>
334 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
335 * </ul>
336 * @exception SWTException <ul>
337 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
338 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
339 * </ul>
340 *
341 * @see VerifyListener
342 * @see #removeVerifyListener
343 *
344 * @since 3.1
345 */
addVerifyListener(VerifyListener listener)346 public void addVerifyListener (VerifyListener listener) {
347 checkWidget ();
348 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
349 TypedListener typedListener = new TypedListener (listener);
350 addListener (SWT.Verify, typedListener);
351 }
352
applyEditSegments()353 void applyEditSegments () {
354 if (--clearSegmentsCount != 0) return;
355 if (!hooks (SWT.Segments) && !filters (SWT.Segments) && (state & HAS_AUTO_DIRECTION) == 0) return;
356 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
357 int length = OS.GetWindowTextLength (hwndText);
358 char [] buffer = new char [length + 1];
359 if (length > 0) OS.GetWindowText (hwndText, buffer, length + 1);
360 String string = new String (buffer, 0, length);
361 /* Get segments */
362 segments = null;
363 Event event = getSegments (string);
364 if (event == null || event.segments == null) return;
365 segments = event.segments;
366 int nSegments = segments.length;
367 if (nSegments == 0) return;
368 char [] segmentsChars = event.segmentsChars;
369 int limit = (int)OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7fffffff;
370 OS.SendMessage (hwndText, OS.EM_SETLIMITTEXT, limit + Math.min (nSegments, LIMIT - limit), 0);
371 length += nSegments;
372 char [] newChars = new char [length + 1];
373 int charCount = 0, segmentCount = 0;
374 char defaultSeparator = getOrientation () == SWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK;
375 while (charCount < length) {
376 if (segmentCount < nSegments && charCount - segmentCount == segments [segmentCount]) {
377 char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars [segmentCount] : defaultSeparator;
378 newChars [charCount++] = separator;
379 segmentCount++;
380 } else if (string != null) {
381 newChars [charCount] = string.charAt (charCount++ - segmentCount);
382 }
383 }
384 while (segmentCount < nSegments) {
385 segments [segmentCount] = charCount - segmentCount;
386 char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars [segmentCount] : defaultSeparator;
387 newChars [charCount++] = separator;
388 segmentCount++;
389 }
390 /* Get the current selection */
391 int [] start = new int [1], end = new int [1];
392 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
393 boolean oldIgnoreCharacter = ignoreCharacter, oldIgnoreModify = ignoreModify;
394 ignoreCharacter = ignoreModify = true;
395 /*
396 * SetWindowText empties the undo buffer and disables undo menu item.
397 * Sending OS.EM_REPLACESEL message instead.
398 */
399 newChars [length] = 0;
400 OS.SendMessage (hwndText, OS.EM_SETSEL, 0, -1);
401 long undo = OS.SendMessage (hwndText, OS.EM_CANUNDO, 0, 0);
402 OS.SendMessage (hwndText, OS.EM_REPLACESEL, undo, newChars);
403 /* Restore selection */
404 start [0] = translateOffset (start [0]);
405 end [0] = translateOffset (end [0]);
406 if (segmentsChars != null && segmentsChars.length > 0) {
407 /*
408 * In addition to enforcing the required direction by prepending a UCC (LRE
409 * or RLE), also set the direction through a Window style.
410 * This is to ensure correct caret movement, and makes sense even when the
411 * UCC was added by an authentic SegmentListener.
412 */
413 int auto = state & HAS_AUTO_DIRECTION;
414 if (segmentsChars[0] == RLE) {
415 super.updateTextDirection(SWT.RIGHT_TO_LEFT);
416 } else if (segmentsChars[0] == LRE) {
417 super.updateTextDirection(SWT.LEFT_TO_RIGHT);
418 }
419 state |= auto;
420 }
421 OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
422 ignoreCharacter = oldIgnoreCharacter;
423 ignoreModify = oldIgnoreModify;
424 }
425
applyListSegments()426 void applyListSegments () {
427 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
428 if (count == OS.CB_ERR) return;
429 boolean add = items.length != count;
430 if (add) items = new String [count];
431 int index = items.length;
432 int selection = OS.CB_ERR;
433 int cp = getCodePage ();
434 String string;
435 TCHAR buffer;
436 if (!noSelection) {
437 selection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
438 }
439 while (index-- > 0) {
440 buffer = null;
441 if (add) {
442 int length = (int)OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, index, 0);
443 if (length == OS.CB_ERR) error (SWT.ERROR);
444 buffer = new TCHAR (cp, length + 1);
445 if (OS.SendMessage (handle, OS.CB_GETLBTEXT, index, buffer) == OS.CB_ERR) return;
446 items [index] = string = buffer.toString (0, length);
447 } else {
448 string = items [index];
449 }
450 if (OS.SendMessage (handle, OS.CB_DELETESTRING, index, 0) == OS.CB_ERR) return;
451 if (buffer == null) buffer = new TCHAR (cp, string, true);
452 if (OS.SendMessage (handle, OS.CB_INSERTSTRING, index, buffer) == OS.CB_ERR) return;
453 }
454 if (selection != OS.CB_ERR) {
455 OS.SendMessage (handle, OS.CB_SETCURSEL, selection, 0);
456 }
457 }
458
459 @Override
callWindowProc(long hwnd, int msg, long wParam, long lParam)460 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
461 if (handle == 0) return 0;
462 if (hwnd == handle) {
463 switch (msg) {
464 case OS.WM_SIZE: {
465 ignoreResize = true;
466 boolean oldLockText = lockText;
467 if ((style & SWT.READ_ONLY) == 0) lockText = true;
468 long result = OS.CallWindowProc (ComboProc, hwnd, msg, wParam, lParam);
469 if ((style & SWT.READ_ONLY) == 0) lockText = oldLockText;
470 ignoreResize = false;
471 return result;
472 }
473 }
474 return OS.CallWindowProc (ComboProc, hwnd, msg, wParam, lParam);
475 }
476 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
477 if (hwnd == hwndText) {
478 if (lockText && msg == OS.WM_SETTEXT) {
479 long hHeap = OS.GetProcessHeap ();
480 int length = OS.GetWindowTextLength (handle);
481 char [] buffer = new char [length + 1];
482 OS.GetWindowText (handle, buffer, length + 1);
483 int byteCount = buffer.length * TCHAR.sizeof;
484 long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
485 OS.MoveMemory (pszText, buffer, byteCount);
486 long code = OS.CallWindowProc (EditProc, hwndText, msg, wParam, pszText);
487 OS.HeapFree (hHeap, 0, pszText);
488 return code;
489 }
490 return OS.CallWindowProc (EditProc, hwnd, msg, wParam, lParam);
491 }
492 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
493 if (hwnd == hwndList) {
494 return OS.CallWindowProc (ListProc, hwnd, msg, wParam, lParam);
495 }
496 return OS.DefWindowProc (hwnd, msg, wParam, lParam);
497 }
498
CBTProc(long nCode, long wParam, long lParam)499 long CBTProc (long nCode, long wParam, long lParam) {
500 if ((int)nCode == OS.HCBT_CREATEWND) {
501 char [] buffer = new char [128];
502 int length = OS.GetClassName (wParam, buffer, buffer.length);
503 String className = new String (buffer, 0, length);
504 if (className.equals ("Edit") || className.equals ("EDIT")) { //$NON-NLS-1$ //$NON-NLS-2$
505 int bits = OS.GetWindowLong (wParam, OS.GWL_STYLE);
506 OS.SetWindowLong (wParam, OS.GWL_STYLE, bits & ~OS.ES_NOHIDESEL);
507 }
508 }
509 return OS.CallNextHookEx (cbtHook, (int)nCode, wParam, lParam);
510 }
511
512 @Override
checkSubclass()513 protected void checkSubclass () {
514 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
515 }
516
checkStyle(int style)517 static int checkStyle (int style) {
518 /*
519 * Feature in Windows. It is not possible to create
520 * a combo box that has a border using Windows style
521 * bits. All combo boxes draw their own border and
522 * do not use the standard Windows border styles.
523 * Therefore, no matter what style bits are specified,
524 * clear the BORDER bits so that the SWT style will
525 * match the Windows widget.
526 *
527 * The Windows behavior is currently implemented on
528 * all platforms.
529 */
530 style &= ~SWT.BORDER;
531
532 /*
533 * Even though it is legal to create this widget
534 * with scroll bars, they serve no useful purpose
535 * because they do not automatically scroll the
536 * widget's client area. The fix is to clear
537 * the SWT style.
538 */
539 style &= ~(SWT.H_SCROLL | SWT.V_SCROLL);
540 style = checkBits (style, SWT.DROP_DOWN, SWT.SIMPLE, 0, 0, 0, 0);
541 if ((style & SWT.SIMPLE) != 0) return style & ~SWT.READ_ONLY;
542 return style;
543 }
544
clearSegments(boolean applyText)545 void clearSegments (boolean applyText) {
546 if (clearSegmentsCount++ != 0) return;
547 if (segments == null) return;
548 int nSegments = segments.length;
549 if (nSegments == 0) return;
550 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
551 int limit = (int)OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7fffffff;
552 if (limit < LIMIT) {
553 OS.SendMessage (hwndText, OS.EM_SETLIMITTEXT, Math.max (1, limit - nSegments), 0);
554 }
555 if (!applyText) {
556 segments = null;
557 return;
558 }
559 boolean oldIgnoreCharacter = ignoreCharacter, oldIgnoreModify = ignoreModify;
560 ignoreCharacter = ignoreModify = true;
561 int length = OS.GetWindowTextLength (hwndText);
562 int cp = getCodePage ();
563 TCHAR buffer = new TCHAR (cp, length + 1);
564 if (length > 0) OS.GetWindowText (hwndText, buffer, length + 1);
565 buffer = deprocessText (buffer, 0, -1, true);
566 /* Get the current selection */
567 int [] start = new int [1], end = new int [1];
568 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
569 start [0] = untranslateOffset (start [0]);
570 end [0] = untranslateOffset (end[0]);
571
572 segments = null;
573 /*
574 * SetWindowText empties the undo buffer and disables undo in the context
575 * menu. Sending OS.EM_REPLACESEL message instead.
576 */
577 OS.SendMessage (hwndText, OS.EM_SETSEL, 0, -1);
578 long undo = OS.SendMessage (hwndText, OS.EM_CANUNDO, 0, 0);
579 OS.SendMessage (hwndText, OS.EM_REPLACESEL, undo, buffer);
580 OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
581 ignoreCharacter = oldIgnoreCharacter;
582 ignoreModify = oldIgnoreModify;
583 }
584
585 /**
586 * Sets the selection in the receiver's text field to an empty
587 * selection starting just before the first character. If the
588 * text field is editable, this has the effect of placing the
589 * i-beam at the start of the text.
590 * <p>
591 * Note: To clear the selected items in the receiver's list,
592 * use <code>deselectAll()</code>.
593 * </p>
594 *
595 * @exception SWTException <ul>
596 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
597 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
598 * </ul>
599 *
600 * @see #deselectAll
601 */
clearSelection()602 public void clearSelection () {
603 checkWidget ();
604 OS.SendMessage (handle, OS.CB_SETEDITSEL, 0, -1);
605 }
606
computeSizeInPixels(int wHint, int hHint, boolean changed)607 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
608 checkWidget ();
609 int width = 0, height = 0;
610 if (wHint == SWT.DEFAULT) {
611 long newFont, oldFont = 0;
612 long hDC = OS.GetDC (handle);
613 newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
614 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
615 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
616 RECT rect = new RECT ();
617 int flags = OS.DT_CALCRECT | OS.DT_NOPREFIX;
618 if ((style & SWT.READ_ONLY) == 0) flags |= OS.DT_EDITCONTROL;
619 int length = OS.GetWindowTextLength (handle);
620 char [] buffer = new char [length + 1];
621 OS.GetWindowText (handle, buffer, length + 1);
622 OS.DrawText (hDC, buffer, length, rect, flags);
623 width = Math.max (width, rect.right - rect.left);
624 if ((style & SWT.H_SCROLL) != 0) {
625 width = Math.max (width, scrollWidth);
626 } else {
627 for (int i=0; i<count; i++) {
628 length = (int)OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, i, 0);
629 if (length != OS.CB_ERR) {
630 if (length + 1 > buffer.length) buffer = new char [length + 1];
631 int result = (int)OS.SendMessage (handle, OS.CB_GETLBTEXT, i, buffer);
632 if (result != OS.CB_ERR) {
633 OS.DrawText (hDC, buffer, length, rect, flags);
634 width = Math.max (width, rect.right - rect.left);
635 }
636 }
637 }
638 }
639 if (newFont != 0) OS.SelectObject (hDC, oldFont);
640 OS.ReleaseDC (handle, hDC);
641 }
642 if (hHint == SWT.DEFAULT) {
643 if ((style & SWT.SIMPLE) != 0) {
644 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
645 int itemHeight = (int)OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, 0, 0);
646 height = count * itemHeight;
647 }
648 }
649 if (width == 0) width = DEFAULT_WIDTH;
650 if (height == 0) height = DEFAULT_HEIGHT;
651 if (wHint != SWT.DEFAULT) width = wHint;
652 if (hHint != SWT.DEFAULT) height = hHint;
653 if ((style & SWT.READ_ONLY) != 0) {
654 width += 8;
655 } else {
656 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
657 if (hwndText != 0) {
658 long margins = OS.SendMessage (hwndText, OS.EM_GETMARGINS, 0, 0);
659 int marginWidth = OS.LOWORD (margins) + OS.HIWORD (margins);
660 width += marginWidth + 3;
661 }
662 }
663 COMBOBOXINFO pcbi = new COMBOBOXINFO ();
664 pcbi.cbSize = COMBOBOXINFO.sizeof;
665 if (((style & SWT.SIMPLE) == 0) && OS.GetComboBoxInfo (handle, pcbi)) {
666 width += pcbi.itemLeft + (pcbi.buttonRight - pcbi.buttonLeft);
667 height = (pcbi.buttonBottom - pcbi.buttonTop) + pcbi.buttonTop * 2;
668 } else {
669 int border = OS.GetSystemMetrics (OS.SM_CXEDGE);
670 width += OS.GetSystemMetrics (OS.SM_CXVSCROLL) + border * 2;
671 int textHeight = (int)OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, -1, 0);
672 if ((style & SWT.DROP_DOWN) != 0) {
673 height = textHeight + 6;
674 } else {
675 height += textHeight + 10;
676 }
677 }
678 if ((style & SWT.SIMPLE) != 0 && (style & SWT.H_SCROLL) != 0) {
679 height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
680 }
681 return new Point (width, height);
682 }
683
684 /**
685 * Copies the selected text.
686 * <p>
687 * The current selection is copied to the clipboard.
688 * </p>
689 *
690 * @exception SWTException <ul>
691 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
692 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
693 * </ul>
694 *
695 * @since 2.1
696 */
copy()697 public void copy () {
698 checkWidget ();
699 OS.SendMessage (handle, OS.WM_COPY, 0, 0);
700 }
701
702 @Override
createHandle()703 void createHandle () {
704 /*
705 * Feature in Windows. When the selection changes in a combo box,
706 * Windows draws the selection, even when the combo box does not
707 * have focus. Strictly speaking, this is the correct Windows
708 * behavior because the combo box sets ES_NOHIDESEL on the text
709 * control that it creates. Despite this, it looks strange because
710 * Windows also clears the selection and selects all the text when
711 * the combo box gets focus. The fix is use the CBT hook to clear
712 * the ES_NOHIDESEL style bit when the text control is created.
713 */
714 if ((style & (SWT.READ_ONLY | SWT.SIMPLE)) != 0) {
715 super.createHandle ();
716 } else {
717 int threadId = OS.GetCurrentThreadId ();
718 Callback cbtCallback = new Callback (this, "CBTProc", 3); //$NON-NLS-1$
719 cbtHook = OS.SetWindowsHookEx (OS.WH_CBT, cbtCallback.getAddress (), 0, threadId);
720 super.createHandle ();
721 if (cbtHook != 0) OS.UnhookWindowsHookEx (cbtHook);
722 cbtHook = 0;
723 cbtCallback.dispose ();
724 }
725 state &= ~(CANVAS | THEME_BACKGROUND);
726
727 stateFlagsUsable = stateFlagsTest();
728
729 /* Get the text and list window procs */
730 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
731 if (hwndText != 0 && EditProc == 0) {
732 EditProc = OS.GetWindowLongPtr (hwndText, OS.GWLP_WNDPROC);
733 }
734 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
735 if (hwndList != 0 && ListProc == 0) {
736 ListProc = OS.GetWindowLongPtr (hwndList, OS.GWLP_WNDPROC);
737 }
738
739 /*
740 * Bug in Windows. If the combo box has the CBS_SIMPLE style,
741 * the list portion of the combo box is not drawn correctly the
742 * first time, causing pixel corruption. The fix is to ensure
743 * that the combo box has been resized more than once.
744 */
745 if ((style & SWT.SIMPLE) != 0) {
746 int flags = OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
747 OS.SetWindowPos (handle, 0, 0, 0, 0x3FFF, 0x3FFF, flags);
748 OS.SetWindowPos (handle, 0, 0, 0, 0, 0, flags);
749 }
750 }
751
752 @Override
createWidget()753 void createWidget() {
754 super.createWidget();
755 visibleCount = VISIBLE_COUNT;
756 if ((style & SWT.SIMPLE) == 0) {
757 int itemHeight = (int)OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, 0, 0);
758 if (itemHeight != OS.CB_ERR && itemHeight != 0) {
759 int maxHeight = 0;
760 long hmonitor = OS.MonitorFromWindow (handle, OS.MONITOR_DEFAULTTONEAREST);
761 MONITORINFO lpmi = new MONITORINFO ();
762 lpmi.cbSize = MONITORINFO.sizeof;
763 OS.GetMonitorInfo (hmonitor, lpmi);
764 maxHeight = (lpmi.rcWork_bottom - lpmi.rcWork_top) / 3;
765 visibleCount = Math.max(visibleCount, maxHeight / itemHeight);
766 }
767 }
768 }
769
770 /**
771 * Cuts the selected text.
772 * <p>
773 * The current selection is first copied to the
774 * clipboard and then deleted from the widget.
775 * </p>
776 *
777 * @exception SWTException <ul>
778 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
779 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
780 * </ul>
781 *
782 * @since 2.1
783 */
cut()784 public void cut () {
785 checkWidget ();
786 if ((style & SWT.READ_ONLY) != 0) return;
787 OS.SendMessage (handle, OS.WM_CUT, 0, 0);
788 }
789
790 @Override
defaultBackground()791 int defaultBackground () {
792 return OS.GetSysColor (OS.COLOR_WINDOW);
793 }
794
deprocessText(TCHAR text, int start, int end, boolean terminate)795 TCHAR deprocessText (TCHAR text, int start, int end, boolean terminate) {
796 if (text == null || segments == null) return text;
797 int length = text.length();
798 if (length == 0) return text;
799 int nSegments = segments.length;
800 if (nSegments == 0) return text;
801 char [] chars;
802 if (start < 0) start = 0;
803 chars = text.chars;
804 if (text.chars [length - 1] == 0) length--;
805 if (end == -1) end = length;
806 if (end > segments [0] && start <= segments [nSegments - 1]) {
807 int nLeadSegments = 0;
808 while (start - nLeadSegments > segments [nLeadSegments]) nLeadSegments++;
809 int segmentCount = nLeadSegments;
810 for (int i = start; i < end; i++) {
811 if (segmentCount < nSegments && i - segmentCount == segments [segmentCount]) {
812 ++segmentCount;
813 } else {
814 chars [i - segmentCount + nLeadSegments] = chars [i];
815 }
816 }
817 length = end - start - segmentCount + nLeadSegments;
818 }
819 if (start != 0 || end != length) {
820 char [] newChars = new char [length];
821 System.arraycopy(chars, start, newChars, 0, length);
822 return new TCHAR (getCodePage (), newChars, terminate);
823 }
824 return text;
825 }
826
827 @Override
deregister()828 void deregister () {
829 super.deregister ();
830 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
831 if (hwndText != 0) display.removeControl (hwndText);
832 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
833 if (hwndList != 0) display.removeControl (hwndList);
834 }
835
836 /**
837 * Deselects the item at the given zero-relative index in the receiver's
838 * list. If the item at the index was already deselected, it remains
839 * deselected. Indices that are out of range are ignored.
840 *
841 * @param index the index of the item to deselect
842 *
843 * @exception SWTException <ul>
844 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
845 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
846 * </ul>
847 */
deselect(int index)848 public void deselect (int index) {
849 checkWidget ();
850 int selection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
851 if (index != selection) return;
852 OS.SendMessage (handle, OS.CB_SETCURSEL, -1, 0);
853 sendEvent (SWT.Modify);
854 // widget could be disposed at this point
855 clearSegments (false);
856 clearSegmentsCount--;
857 }
858
859 /**
860 * Deselects all selected items in the receiver's list.
861 * <p>
862 * Note: To clear the selection in the receiver's text field,
863 * use <code>clearSelection()</code>.
864 * </p>
865 *
866 * @exception SWTException <ul>
867 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
868 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
869 * </ul>
870 *
871 * @see #clearSelection
872 */
deselectAll()873 public void deselectAll () {
874 checkWidget ();
875 OS.SendMessage (handle, OS.CB_SETCURSEL, -1, 0);
876 sendEvent (SWT.Modify);
877 // widget could be disposed at this point
878 clearSegments (false);
879 clearSegmentsCount--;
880 }
881
882 @Override
dragDetect(long hwnd, int x, int y, boolean filter, boolean [] detect, boolean [] consume)883 boolean dragDetect (long hwnd, int x, int y, boolean filter, boolean [] detect, boolean [] consume) {
884 if (filter && (style & SWT.READ_ONLY) == 0) {
885 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
886 if (hwndText != 0) {
887 int [] start = new int [1], end = new int [1];
888 OS.SendMessage (handle, OS.CB_GETEDITSEL, start, end);
889 if (start [0] != end [0]) {
890 long lParam = OS.MAKELPARAM (x, y);
891 int position = OS.LOWORD (OS.SendMessage (hwndText, OS.EM_CHARFROMPOS, 0, lParam));
892 if (start [0] <= position && position < end [0]) {
893 if (super.dragDetect (hwnd, x, y, filter, detect, consume)) {
894 if (consume != null) consume [0] = true;
895 return true;
896 }
897 }
898 }
899 return false;
900 }
901 }
902 return super.dragDetect (hwnd, x, y, filter, detect, consume);
903 }
904
905 /**
906 * Returns a point describing the location of the caret relative
907 * to the receiver.
908 *
909 * @return a point, the location of the caret
910 *
911 * @exception SWTException <ul>
912 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
913 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
914 * </ul>
915 *
916 * @since 3.8
917 */
getCaretLocation()918 public Point getCaretLocation () {
919 checkWidget ();
920 return DPIUtil.autoScaleDown(getCaretLocationInPixels());
921 }
922
getCaretLocationInPixels()923 Point getCaretLocationInPixels () {
924 /*
925 * Bug in Windows. For some reason, Windows is unable
926 * to return the pixel coordinates of the last character
927 * in the widget. The fix is to temporarily insert a
928 * space, query the coordinates and delete the space.
929 * The selection is always an i-beam in this case because
930 * this is the only time the start of the selection can
931 * be equal to the last character position in the widget.
932 * If EM_POSFROMCHAR fails for any other reason, return
933 * pixel coordinates (0,0).
934 */
935 int position = translateOffset (getCaretPosition ());
936 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
937 long caretPos = OS.SendMessage (hwndText, OS.EM_POSFROMCHAR, position, 0);
938 if (caretPos == -1) {
939 caretPos = 0;
940 if (position >= OS.GetWindowTextLength (hwndText)) {
941 int [] start = new int [1], end = new int [1];
942 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
943 OS.SendMessage (hwndText, OS.EM_SETSEL, position, position);
944 /*
945 * Feature in Windows. When an edit control with ES_MULTILINE
946 * style that does not have the WS_VSCROLL style is full (i.e.
947 * there is no space at the end to draw any more characters),
948 * EM_REPLACESEL sends a WM_CHAR with a backspace character
949 * to remove any further text that is added. This is an
950 * implementation detail of the edit control that is unexpected
951 * and can cause endless recursion when EM_REPLACESEL is sent
952 * from a WM_CHAR handler. The fix is to ignore calling the
953 * handler from WM_CHAR.
954 */
955 ignoreCharacter = ignoreModify = true;
956 OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, new char [] {' ', '\0'});
957 caretPos = OS.SendMessage (hwndText, OS.EM_POSFROMCHAR, position, 0);
958 OS.SendMessage (hwndText, OS.EM_SETSEL, position, position + 1);
959 OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, new char [1]);
960 ignoreCharacter = ignoreModify = false;
961 OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], start [0]);
962 OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
963 }
964 }
965 POINT point = new POINT ();
966 point.x = OS.GET_X_LPARAM (caretPos);
967 point.y = OS.GET_Y_LPARAM (caretPos);
968 OS.MapWindowPoints (hwndText, handle, point, 1);
969 return new Point (point.x, point.y);
970 }
971
972 /**
973 * Returns the character position of the caret.
974 * <p>
975 * Indexing is zero based.
976 * </p>
977 *
978 * @return the position of the caret
979 *
980 * @exception SWTException <ul>
981 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
982 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
983 * </ul>
984 *
985 * @since 3.8
986 */
getCaretPosition()987 public int getCaretPosition () {
988 checkWidget ();
989 int [] start = new int [1], end = new int [1];
990 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
991 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
992 /*
993 * In Windows, there is no API to get the position of the caret
994 * when the selection is not an i-beam. The best that can be done
995 * is to query the pixel position of the current caret and compare
996 * it to the pixel position of the start and end of the selection.
997 *
998 * NOTE: This does not work when the i-beam belongs to another
999 * control. In this case, guess that the i-beam is at the start
1000 * of the selection.
1001 */
1002 int caret = start [0];
1003 if (start [0] != end [0]) {
1004 int idThread = OS.GetWindowThreadProcessId (hwndText, null);
1005 GUITHREADINFO lpgui = new GUITHREADINFO ();
1006 lpgui.cbSize = GUITHREADINFO.sizeof;
1007 if (OS.GetGUIThreadInfo (idThread, lpgui)) {
1008 if (lpgui.hwndCaret == hwndText || lpgui.hwndCaret == 0) {
1009 POINT ptCurrentPos = new POINT ();
1010 if (OS.GetCaretPos (ptCurrentPos)) {
1011 long endPos = OS.SendMessage (hwndText, OS.EM_POSFROMCHAR, end [0], 0);
1012 if (endPos == -1) {
1013 long startPos = OS.SendMessage (hwndText, OS.EM_POSFROMCHAR, start [0], 0);
1014 int startX = OS.GET_X_LPARAM (startPos);
1015 if (ptCurrentPos.x > startX) caret = end [0];
1016 } else {
1017 int endX = OS.GET_X_LPARAM (endPos);
1018 if (ptCurrentPos.x >= endX) caret = end [0];
1019 }
1020 }
1021 }
1022 }
1023 }
1024 return untranslateOffset (caret);
1025 }
1026
1027 /**
1028 * Returns the item at the given, zero-relative index in the
1029 * receiver's list. Throws an exception if the index is out
1030 * of range.
1031 *
1032 * @param index the index of the item to return
1033 * @return the item at the given index
1034 *
1035 * @exception IllegalArgumentException <ul>
1036 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1037 * </ul>
1038 * @exception SWTException <ul>
1039 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1040 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1041 * </ul>
1042 */
getItem(int index)1043 public String getItem (int index) {
1044 checkWidget ();
1045 int length = (int)OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, index, 0);
1046 if (length != OS.CB_ERR) {
1047 if (hooks (SWT.Segments) || filters (SWT.Segments) || (state & HAS_AUTO_DIRECTION) != 0) return items [index];
1048 char [] buffer = new char [length + 1];
1049 int result = (int)OS.SendMessage (handle, OS.CB_GETLBTEXT, index, buffer);
1050 if (result != OS.CB_ERR) return new String (buffer, 0, length);
1051 }
1052 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1053 if (0 <= index && index < count) error (SWT.ERROR_CANNOT_GET_ITEM);
1054 error (SWT.ERROR_INVALID_RANGE);
1055 return "";
1056 }
1057
1058 /**
1059 * Returns the number of items contained in the receiver's list.
1060 *
1061 * @return the number of items
1062 *
1063 * @exception SWTException <ul>
1064 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1065 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1066 * </ul>
1067 */
getItemCount()1068 public int getItemCount () {
1069 checkWidget ();
1070 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1071 if (count == OS.CB_ERR) error (SWT.ERROR_CANNOT_GET_COUNT);
1072 return count;
1073 }
1074
1075 /**
1076 * Returns the height of the area which would be used to
1077 * display <em>one</em> of the items in the receiver's list.
1078 *
1079 * @return the height of one item
1080 *
1081 * @exception SWTException <ul>
1082 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1083 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1084 * </ul>
1085 */
getItemHeight()1086 public int getItemHeight () {
1087 checkWidget ();
1088 return DPIUtil.autoScaleDown(getItemHeightInPixels());
1089 }
1090
getItemHeightInPixels()1091 int getItemHeightInPixels () {
1092 int result = (int)OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, 0, 0);
1093 if (result == OS.CB_ERR) error (SWT.ERROR_CANNOT_GET_ITEM_HEIGHT);
1094 return result;
1095 }
1096
1097 /**
1098 * Returns a (possibly empty) array of <code>String</code>s which are
1099 * the items in the receiver's list.
1100 * <p>
1101 * Note: This is not the actual structure used by the receiver
1102 * to maintain its list of items, so modifying the array will
1103 * not affect the receiver.
1104 * </p>
1105 *
1106 * @return the items in the receiver's list
1107 *
1108 * @exception SWTException <ul>
1109 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1110 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1111 * </ul>
1112 */
getItems()1113 public String [] getItems () {
1114 checkWidget ();
1115 String [] result;
1116 int count = getItemCount ();
1117 result = new String [count];
1118 for (int i=0; i<count; i++) result [i] = getItem (i);
1119 return result;
1120 }
1121
1122 /**
1123 * Returns <code>true</code> if the receiver's list is visible,
1124 * and <code>false</code> otherwise.
1125 * <p>
1126 * If one of the receiver's ancestors is not visible or some
1127 * other condition makes the receiver not visible, this method
1128 * may still indicate that it is considered visible even though
1129 * it may not actually be showing.
1130 * </p>
1131 *
1132 * @return the receiver's list's visibility state
1133 *
1134 * @exception SWTException <ul>
1135 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1136 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1137 * </ul>
1138 *
1139 * @since 3.4
1140 */
getListVisible()1141 public boolean getListVisible () {
1142 checkWidget ();
1143 if ((style & SWT.DROP_DOWN) != 0) {
1144 return OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0;
1145 }
1146 return true;
1147 }
1148
1149 @Override
getNameText()1150 String getNameText () {
1151 return getText ();
1152 }
1153
1154 /**
1155 * Marks the receiver's list as visible if the argument is <code>true</code>,
1156 * and marks it invisible otherwise.
1157 * <p>
1158 * If one of the receiver's ancestors is not visible or some
1159 * other condition makes the receiver not visible, marking
1160 * it visible may not actually cause it to be displayed.
1161 * </p>
1162 *
1163 * @param visible the new visibility state
1164 *
1165 * @exception SWTException <ul>
1166 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1167 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1168 * </ul>
1169 *
1170 * @since 3.4
1171 */
setListVisible(boolean visible)1172 public void setListVisible (boolean visible) {
1173 checkWidget ();
1174 OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, visible ? 1 : 0, 0);
1175 }
1176
1177 /**
1178 * Returns the orientation of the receiver.
1179 *
1180 * @return the orientation style
1181 *
1182 * @exception SWTException <ul>
1183 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1184 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1185 * </ul>
1186 *
1187 * @since 2.1.2
1188 */
1189 @Override
getOrientation()1190 public int getOrientation () {
1191 return super.getOrientation ();
1192 }
1193
getSegments(String string)1194 Event getSegments (String string) {
1195 Event event = null;
1196 if (hooks (SWT.Segments) || filters (SWT.Segments)) {
1197 event = new Event ();
1198 event.text = string;
1199 sendEvent (SWT.Segments, event);
1200 if (event != null && event.segments != null) {
1201 for (int i = 1, segmentCount = event.segments.length, lineLength = string == null ? 0 : string.length(); i < segmentCount; i++) {
1202 if (event.segments[i] < event.segments[i - 1] || event.segments[i] > lineLength) {
1203 SWT.error (SWT.ERROR_INVALID_ARGUMENT);
1204 }
1205 }
1206 }
1207 }
1208 if ((state & HAS_AUTO_DIRECTION) != 0) {
1209 int direction = BidiUtil.resolveTextDirection(string);
1210 if (direction == SWT.NONE) {
1211 /*
1212 * Force adding a UCC even when no strong characters are found.
1213 * Otherwise, the widget would keep the old direction, which might be
1214 * inappropriate for the new text.
1215 */
1216 direction = (style & SWT.RIGHT_TO_LEFT) != 0 ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
1217 }
1218 int [] oldSegments = null;
1219 char [] oldSegmentsChars = null;
1220 if (event == null) {
1221 event = new Event ();
1222 } else {
1223 oldSegments = event.segments;
1224 oldSegmentsChars = event.segmentsChars;
1225 }
1226 int nSegments = oldSegments == null ? 0 : oldSegments.length;
1227 event.segments = new int [nSegments + 1];
1228 event.segmentsChars = new char [nSegments + 1];
1229 if (oldSegments != null) {
1230 System.arraycopy(oldSegments, 0, event.segments, 1, nSegments);
1231 }
1232 if (oldSegmentsChars != null) {
1233 System.arraycopy(oldSegmentsChars, 0, event.segmentsChars, 1, nSegments);
1234 }
1235 event.segments [0] = 0;
1236 event.segmentsChars [0] = direction == SWT.RIGHT_TO_LEFT ? RLE : LRE;
1237 }
1238 return event;
1239 }
1240
getSegmentsText(String text, Event event)1241 String getSegmentsText (String text, Event event) {
1242 if (text == null || event == null) return text;
1243 int[] segments = event.segments;
1244 if (segments == null) return text;
1245 int nSegments = segments.length;
1246 if (nSegments == 0) return text;
1247 char[] segmentsChars = /*event == null ? this.segmentsChars : */event.segmentsChars;
1248 int length = text.length();
1249 char[] oldChars = new char[length];
1250 text.getChars (0, length, oldChars, 0);
1251 char[] newChars = new char[length + nSegments];
1252 int charCount = 0, segmentCount = 0;
1253 char defaultSeparator = getOrientation () == SWT.RIGHT_TO_LEFT ? RTL_MARK : LTR_MARK;
1254 while (charCount < length) {
1255 if (segmentCount < nSegments && charCount == segments[segmentCount]) {
1256 char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars[segmentCount] : defaultSeparator;
1257 newChars[charCount + segmentCount++] = separator;
1258 } else {
1259 newChars[charCount + segmentCount] = oldChars[charCount++];
1260 }
1261 }
1262 while (segmentCount < nSegments) {
1263 segments[segmentCount] = charCount;
1264 char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars[segmentCount] : defaultSeparator;
1265 newChars[charCount + segmentCount++] = separator;
1266 }
1267 return new String(newChars, 0, newChars.length);
1268 }
1269
1270 /**
1271 * Returns a <code>Point</code> whose x coordinate is the
1272 * character position representing the start of the selection
1273 * in the receiver's text field, and whose y coordinate is the
1274 * character position representing the end of the selection.
1275 * An "empty" selection is indicated by the x and y coordinates
1276 * having the same value.
1277 * <p>
1278 * Indexing is zero based. The range of a selection is from
1279 * 0..N where N is the number of characters in the widget.
1280 * </p>
1281 *
1282 * @return a point representing the selection start and end
1283 *
1284 * @exception SWTException <ul>
1285 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1286 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1287 * </ul>
1288 */
getSelection()1289 public Point getSelection () {
1290 checkWidget ();
1291 if ((style & SWT.DROP_DOWN) != 0 && (style & SWT.READ_ONLY) != 0) {
1292 return new Point (0, OS.GetWindowTextLength (handle));
1293 }
1294 int [] start = new int [1], end = new int [1];
1295 OS.SendMessage (handle, OS.CB_GETEDITSEL, start, end);
1296 return new Point (untranslateOffset (start [0]), untranslateOffset (end [0]));
1297 }
1298
1299 /**
1300 * Returns the zero-relative index of the item which is currently
1301 * selected in the receiver's list, or -1 if no item is selected.
1302 *
1303 * @return the index of the selected item
1304 *
1305 * @exception SWTException <ul>
1306 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1307 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1308 * </ul>
1309 */
getSelectionIndex()1310 public int getSelectionIndex () {
1311 checkWidget ();
1312 if (noSelection) return -1;
1313 return (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
1314 }
1315
1316 /**
1317 * Returns a string containing a copy of the contents of the
1318 * receiver's text field, or an empty string if there are no
1319 * contents.
1320 *
1321 * @return the receiver's text
1322 *
1323 * @exception SWTException <ul>
1324 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1325 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1326 * </ul>
1327 */
getText()1328 public String getText () {
1329 checkWidget ();
1330 int length = OS.GetWindowTextLength (handle);
1331 if (length == 0) return "";
1332 TCHAR buffer = new TCHAR (getCodePage (), length + 1);
1333 OS.GetWindowText (handle, buffer, length + 1);
1334 if (segments != null) {
1335 buffer = deprocessText (buffer, 0, -1, false);
1336 return buffer.toString ();
1337 }
1338
1339 return buffer.toString (0, length);
1340 }
1341
1342 /**
1343 * Returns the height of the receivers's text field.
1344 *
1345 * @return the text height
1346 *
1347 * @exception SWTException <ul>
1348 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1349 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1350 * </ul>
1351 */
getTextHeight()1352 public int getTextHeight () {
1353 checkWidget ();
1354 return DPIUtil.autoScaleDown(getTextHeightInPixels());
1355 }
1356
getTextHeightInPixels()1357 int getTextHeightInPixels () {
1358 COMBOBOXINFO pcbi = new COMBOBOXINFO ();
1359 pcbi.cbSize = COMBOBOXINFO.sizeof;
1360 if (((style & SWT.SIMPLE) == 0) && OS.GetComboBoxInfo (handle, pcbi)) {
1361 return (pcbi.buttonBottom - pcbi.buttonTop) + pcbi.buttonTop * 2;
1362 }
1363 int result = (int)OS.SendMessage (handle, OS.CB_GETITEMHEIGHT, -1, 0);
1364 if (result == OS.CB_ERR) error (SWT.ERROR_CANNOT_GET_ITEM_HEIGHT);
1365 return (style & SWT.DROP_DOWN) != 0 ? result + 6 : result + 10;
1366 }
1367
1368 /**
1369 * Returns the maximum number of characters that the receiver's
1370 * text field is capable of holding. If this has not been changed
1371 * by <code>setTextLimit()</code>, it will be the constant
1372 * <code>Combo.LIMIT</code>.
1373 *
1374 * @return the text limit
1375 *
1376 * @exception SWTException <ul>
1377 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1378 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1379 * </ul>
1380 *
1381 * @see #LIMIT
1382 */
getTextLimit()1383 public int getTextLimit () {
1384 checkWidget ();
1385 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
1386 if (hwndText == 0) return LIMIT;
1387 int limit = (int)OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF;
1388 if (segments != null && limit < LIMIT) limit = Math.max (1, limit - segments.length);
1389 return limit;
1390 }
1391
1392 /**
1393 * Gets the number of items that are visible in the drop
1394 * down portion of the receiver's list.
1395 * <p>
1396 * Note: This operation is a hint and is not supported on
1397 * platforms that do not have this concept.
1398 * </p>
1399 *
1400 * @return the number of items that are visible
1401 *
1402 * @exception SWTException <ul>
1403 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1404 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1405 * </ul>
1406 *
1407 * @since 3.0
1408 */
getVisibleItemCount()1409 public int getVisibleItemCount () {
1410 checkWidget ();
1411 return visibleCount;
1412 }
1413
1414 @Override
hasFocus()1415 boolean hasFocus () {
1416 long hwndFocus = OS.GetFocus ();
1417 if (hwndFocus == handle) return true;
1418 if (hwndFocus == 0) return false;
1419 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
1420 if (hwndFocus == hwndText) return true;
1421 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
1422 if (hwndFocus == hwndList) return true;
1423 return false;
1424 }
1425
1426 /**
1427 * Searches the receiver's list starting at the first item
1428 * (index 0) until an item is found that is equal to the
1429 * argument, and returns the index of that item. If no item
1430 * is found, returns -1.
1431 *
1432 * @param string the search item
1433 * @return the index of the item
1434 *
1435 * @exception IllegalArgumentException <ul>
1436 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
1437 * </ul>
1438 * @exception SWTException <ul>
1439 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1440 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1441 * </ul>
1442 */
indexOf(String string)1443 public int indexOf (String string) {
1444 return indexOf (string, 0);
1445 }
1446
1447 /**
1448 * Searches the receiver's list starting at the given,
1449 * zero-relative index until an item is found that is equal
1450 * to the argument, and returns the index of that item. If
1451 * no item is found or the starting index is out of range,
1452 * returns -1.
1453 *
1454 * @param string the search item
1455 * @param start the zero-relative index at which to begin the search
1456 * @return the index of the item
1457 *
1458 * @exception IllegalArgumentException <ul>
1459 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
1460 * </ul>
1461 * @exception SWTException <ul>
1462 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1463 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1464 * </ul>
1465 */
indexOf(String string, int start)1466 public int indexOf (String string, int start) {
1467 checkWidget ();
1468 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
1469
1470 /*
1471 * Bug in Windows. For some reason, CB_FINDSTRINGEXACT
1472 * will not find empty strings even though it is legal
1473 * to insert an empty string into a combo. The fix is
1474 * to search the combo, an item at a time.
1475 */
1476 if (string.length () == 0) {
1477 int count = getItemCount ();
1478 for (int i=start; i<count; i++) {
1479 if (string.equals (getItem (i))) return i;
1480 }
1481 return -1;
1482 }
1483
1484 /* Use CB_FINDSTRINGEXACT to search for the item */
1485 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1486 if (!(0 <= start && start < count)) return -1;
1487 int index = start - 1, last = 0;
1488 TCHAR buffer = new TCHAR (getCodePage (), string, true);
1489 do {
1490 index = (int)OS.SendMessage (handle, OS.CB_FINDSTRINGEXACT, last = index, buffer);
1491 if (index == OS.CB_ERR || index <= last) return -1;
1492 } while (!string.equals (getItem (index)));
1493 return index;
1494 }
1495
1496 /**
1497 * Pastes text from clipboard.
1498 * <p>
1499 * The selected text is deleted from the widget
1500 * and new text inserted from the clipboard.
1501 * </p>
1502 *
1503 * @exception SWTException <ul>
1504 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1505 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1506 * </ul>
1507 *
1508 * @since 2.1
1509 */
paste()1510 public void paste () {
1511 checkWidget ();
1512 if ((style & SWT.READ_ONLY) != 0) return;
1513 OS.SendMessage (handle, OS.WM_PASTE, 0, 0);
1514 }
1515
stateFlagsAdd(int flags)1516 void stateFlagsAdd(int flags) {
1517 final long tagCBoxPtr = OS.GetWindowLongPtr(handle, 0);
1518 /*
1519 * Bug 550423: When non-XP-theme COMMCTL32.DLL gets loaded, undocumented
1520 * internal data is not there. We do not support that and in such case
1521 * GetWindowLongPtr function fails and return zero.
1522 */
1523 if (tagCBoxPtr == 0) return;
1524 final long stateFlagsPtr = tagCBoxPtr + stateFlagsOffset;
1525
1526 int stateFlags[] = new int[1];
1527 OS.MoveMemory(stateFlags, stateFlagsPtr, 4);
1528 stateFlags[0] |= flags;
1529 OS.MoveMemory(stateFlagsPtr, stateFlags, 4);
1530 }
1531
1532 /*
1533 * Verify that undocumented internal data is in expected location.
1534 * The test is performed at creation time, when the value of state flags is predictable.
1535 * For simplicity, only SWT.READ_ONLY combos are handled.
1536 */
stateFlagsTest()1537 boolean stateFlagsTest() {
1538 final long tagCBoxPtr = OS.GetWindowLongPtr(handle, 0);
1539 /*
1540 * Bug 550423: When non-XP-theme COMMCTL32.DLL gets loaded, undocumented
1541 * internal data is not there. We do not support that and in such case
1542 * GetWindowLongPtr function fails and return zero.
1543 */
1544 if (tagCBoxPtr == 0) return false;
1545 final long stateFlagsPtr = tagCBoxPtr + stateFlagsOffset;
1546
1547 int stateFlags[] = new int[1];
1548 OS.MoveMemory(stateFlags, stateFlagsPtr, 4);
1549
1550 /*
1551 * 0x00000002 is unknown
1552 * 0x00002000 is set in WM_NCCREATE
1553 * 0x00004000 means CBS_DROPDOWNLIST (SWT.READ_ONLY)
1554 * 0x02000000 is set in WM_NCCREATE and reset after first WM_PAINT
1555 */
1556 return (stateFlags[0] == 0x02006002);
1557 }
1558
1559 @Override
register()1560 void register () {
1561 super.register ();
1562 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
1563 if (hwndText != 0) display.addControl (hwndText, this);
1564 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
1565 if (hwndList != 0) display.addControl (hwndList, this);
1566 }
1567
1568 /**
1569 * Removes the item from the receiver's list at the given
1570 * zero-relative index.
1571 *
1572 * @param index the index for the item
1573 *
1574 * @exception IllegalArgumentException <ul>
1575 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1576 * </ul>
1577 * @exception SWTException <ul>
1578 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1579 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1580 * </ul>
1581 */
remove(int index)1582 public void remove (int index) {
1583 checkWidget ();
1584 remove (index, true);
1585 }
1586
remove(int index, boolean notify)1587 void remove (int index, boolean notify) {
1588 char [] buffer = null;
1589 if ((style & SWT.H_SCROLL) != 0) {
1590 int length = (int)OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, index, 0);
1591 if (length == OS.CB_ERR) {
1592 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1593 if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED);
1594 error (SWT.ERROR_INVALID_RANGE);
1595 }
1596 buffer = new char [length + 1];
1597 int result = (int)OS.SendMessage (handle, OS.CB_GETLBTEXT, index, buffer);
1598 if (result == OS.CB_ERR) {
1599 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1600 if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED);
1601 error (SWT.ERROR_INVALID_RANGE);
1602 }
1603 }
1604 int length = OS.GetWindowTextLength (handle);
1605 int code = (int)OS.SendMessage (handle, OS.CB_DELETESTRING, index, 0);
1606 if (code == OS.CB_ERR) {
1607 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1608 if (0 <= index && index < count) error (SWT.ERROR_ITEM_NOT_REMOVED);
1609 error (SWT.ERROR_INVALID_RANGE);
1610 }
1611 else if (code == 0) {
1612 /*
1613 * Bug in Windows, when combo had exactly one item, that was
1614 * currently selected & is removed, the combo box does not clear the
1615 * text area. The fix is to reset contents of the Combo. Bug#440671
1616 */
1617 OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0);
1618 }
1619 if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer, true);
1620 if (notify && length != OS.GetWindowTextLength (handle)) {
1621 sendEvent (SWT.Modify);
1622 if (isDisposed ()) return;
1623 }
1624 }
1625
1626 /**
1627 * Removes the items from the receiver's list which are
1628 * between the given zero-relative start and end
1629 * indices (inclusive).
1630 *
1631 * @param start the start of the range
1632 * @param end the end of the range
1633 *
1634 * @exception IllegalArgumentException <ul>
1635 * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1636 * </ul>
1637 * @exception SWTException <ul>
1638 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1639 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1640 * </ul>
1641 */
remove(int start, int end)1642 public void remove (int start, int end) {
1643 checkWidget ();
1644 if (start > end) return;
1645 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1646 if (!(0 <= start && start <= end && end < count)) {
1647 error (SWT.ERROR_INVALID_RANGE);
1648 }
1649 int textLength = OS.GetWindowTextLength (handle);
1650 RECT rect = null;
1651 long hDC = 0, oldFont = 0, newFont = 0;
1652 int newWidth = 0;
1653 if ((style & SWT.H_SCROLL) != 0) {
1654 rect = new RECT ();
1655 hDC = OS.GetDC (handle);
1656 newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
1657 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
1658 }
1659 int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
1660 for (int i=start; i<=end; i++) {
1661 char [] buffer = null;
1662 if ((style & SWT.H_SCROLL) != 0) {
1663 int length = (int)OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, start, 0);
1664 if (length == OS.CB_ERR) break;
1665 buffer = new char [length + 1];
1666 int result = (int)OS.SendMessage (handle, OS.CB_GETLBTEXT, start, buffer);
1667 if (result == OS.CB_ERR) break;
1668 }
1669 int result = (int)OS.SendMessage (handle, OS.CB_DELETESTRING, start, 0);
1670 if (result == OS.CB_ERR) {
1671 error (SWT.ERROR_ITEM_NOT_REMOVED);
1672 }
1673 else if (result == 0) {
1674 /*
1675 * Bug in Windows, when combo had exactly one item, that was
1676 * currently selected & is removed, the combo box does not clear the
1677 * text area. The fix is to reset contents of the Combo. Bug#440671
1678 */
1679 OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0);
1680 }
1681 if ((style & SWT.H_SCROLL) != 0) {
1682 OS.DrawText (hDC, buffer, -1, rect, flags);
1683 newWidth = Math.max (newWidth, rect.right - rect.left);
1684 }
1685 }
1686 if ((style & SWT.H_SCROLL) != 0) {
1687 if (newFont != 0) OS.SelectObject (hDC, oldFont);
1688 OS.ReleaseDC (handle, hDC);
1689 setScrollWidth (newWidth, false);
1690 }
1691 if (textLength != OS.GetWindowTextLength (handle)) {
1692 sendEvent (SWT.Modify);
1693 if (isDisposed ()) return;
1694 }
1695 }
1696
1697 /**
1698 * Searches the receiver's list starting at the first item
1699 * until an item is found that is equal to the argument,
1700 * and removes that item from the list.
1701 *
1702 * @param string the item to remove
1703 *
1704 * @exception IllegalArgumentException <ul>
1705 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
1706 * <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li>
1707 * </ul>
1708 * @exception SWTException <ul>
1709 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1710 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1711 * </ul>
1712 */
remove(String string)1713 public void remove (String string) {
1714 checkWidget ();
1715 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
1716 int index = indexOf (string, 0);
1717 if (index == -1) error (SWT.ERROR_INVALID_ARGUMENT);
1718 remove (index);
1719 }
1720
1721 /**
1722 * Removes all of the items from the receiver's list and clear the
1723 * contents of receiver's text field.
1724 * @exception SWTException <ul>
1725 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1726 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1727 * </ul>
1728 */
removeAll()1729 public void removeAll () {
1730 checkWidget ();
1731 OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0);
1732 sendEvent (SWT.Modify);
1733 if (isDisposed ()) return;
1734 if ((style & SWT.H_SCROLL) != 0) setScrollWidth (0);
1735 }
1736
1737 /**
1738 * Removes the listener from the collection of listeners who will
1739 * be notified when the receiver's text is modified.
1740 *
1741 * @param listener the listener which should no longer be notified
1742 *
1743 * @exception IllegalArgumentException <ul>
1744 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1745 * </ul>
1746 * @exception SWTException <ul>
1747 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1748 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1749 * </ul>
1750 *
1751 * @see ModifyListener
1752 * @see #addModifyListener
1753 */
removeModifyListener(ModifyListener listener)1754 public void removeModifyListener (ModifyListener listener) {
1755 checkWidget ();
1756 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
1757 if (eventTable == null) return;
1758 eventTable.unhook (SWT.Modify, listener);
1759 }
1760
1761 /**
1762 * Removes the listener from the collection of listeners who will
1763 * be notified when the receiver's text is modified.
1764 *
1765 * @param listener the listener which should no longer be notified
1766 *
1767 * @exception IllegalArgumentException <ul>
1768 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1769 * </ul>
1770 * @exception SWTException <ul>
1771 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1772 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1773 * </ul>
1774 *
1775 * @see SegmentEvent
1776 * @see SegmentListener
1777 * @see #addSegmentListener
1778 *
1779 * @since 3.103
1780 */
removeSegmentListener(SegmentListener listener)1781 public void removeSegmentListener (SegmentListener listener) {
1782 checkWidget ();
1783 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
1784 eventTable.unhook (SWT.Segments, listener);
1785 int selection = OS.CB_ERR;
1786 if (!noSelection) {
1787 selection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
1788 }
1789 clearSegments (true);
1790 applyEditSegments ();
1791 applyListSegments ();
1792 if (selection != OS.CB_ERR) {
1793 OS.SendMessage (handle, OS.CB_SETCURSEL, selection, 0);
1794 }
1795 }
1796 /**
1797 * Removes the listener from the collection of listeners who will
1798 * be notified when the user changes the receiver's selection.
1799 *
1800 * @param listener the listener which should no longer be notified
1801 *
1802 * @exception IllegalArgumentException <ul>
1803 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1804 * </ul>
1805 * @exception SWTException <ul>
1806 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1807 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1808 * </ul>
1809 *
1810 * @see SelectionListener
1811 * @see #addSelectionListener
1812 */
removeSelectionListener(SelectionListener listener)1813 public void removeSelectionListener (SelectionListener listener) {
1814 checkWidget ();
1815 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
1816 if (eventTable == null) return;
1817 eventTable.unhook (SWT.Selection, listener);
1818 eventTable.unhook (SWT.DefaultSelection,listener);
1819 }
1820
1821 /**
1822 * Removes the listener from the collection of listeners who will
1823 * be notified when the control is verified.
1824 *
1825 * @param listener the listener which should no longer be notified
1826 *
1827 * @exception IllegalArgumentException <ul>
1828 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1829 * </ul>
1830 * @exception SWTException <ul>
1831 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1832 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1833 * </ul>
1834 *
1835 * @see VerifyListener
1836 * @see #addVerifyListener
1837 *
1838 * @since 3.1
1839 */
removeVerifyListener(VerifyListener listener)1840 public void removeVerifyListener (VerifyListener listener) {
1841 checkWidget ();
1842 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
1843 if (eventTable == null) return;
1844 eventTable.unhook (SWT.Verify, listener);
1845 }
1846
1847 @Override
sendKeyEvent(int type, int msg, long wParam, long lParam, Event event)1848 boolean sendKeyEvent (int type, int msg, long wParam, long lParam, Event event) {
1849 if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) {
1850 return false;
1851 }
1852 if ((style & SWT.READ_ONLY) != 0) return true;
1853 if (type != SWT.KeyDown) return true;
1854 if (msg != OS.WM_CHAR && msg != OS.WM_KEYDOWN && msg != OS.WM_IME_CHAR) {
1855 return true;
1856 }
1857 if (event.character == 0) return true;
1858 if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return true;
1859 char key = event.character;
1860 int stateMask = event.stateMask;
1861
1862 /*
1863 * Disable all magic keys that could modify the text
1864 * and don't send events when Alt, Shift or Ctrl is
1865 * pressed.
1866 */
1867 switch (msg) {
1868 case OS.WM_CHAR:
1869 if (key != 0x08 && key != 0x7F && key != '\r' && key != '\t' && key != '\n') break;
1870 // FALL THROUGH
1871 case OS.WM_KEYDOWN:
1872 if ((stateMask & (SWT.ALT | SWT.SHIFT | SWT.CONTROL)) != 0) return false;
1873 break;
1874 }
1875
1876 /*
1877 * Feature in Windows. If the left button is down in
1878 * the text widget, it refuses the character. The fix
1879 * is to detect this case and avoid sending a verify
1880 * event.
1881 */
1882 if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
1883 if (OS.GetDlgItem (handle, CBID_EDIT) == OS.GetCapture()) return true;
1884 }
1885
1886 /* Verify the character */
1887 String oldText = "";
1888 int [] start = new int [1], end = new int [1];
1889 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
1890 if (hwndText == 0) return true;
1891 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
1892 switch (key) {
1893 case 0x08: /* Bs */
1894 if (start [0] == end [0]) {
1895 if (start [0] == 0) return true;
1896 start [0] = start [0] - 1;
1897 start [0] = Math.max (start [0], 0);
1898 }
1899 break;
1900 case 0x7F: /* Del */
1901 if (start [0] == end [0]) {
1902 int length = OS.GetWindowTextLength (hwndText);
1903 if (start [0] == length) return true;
1904 end [0] = end [0] + 1;
1905 end [0] = Math.min (end [0], length);
1906 }
1907 break;
1908 case '\r': /* Return */
1909 return true;
1910 default: /* Tab and other characters */
1911 if (key != '\t' && key < 0x20) return true;
1912 oldText = new String (new char [] {key});
1913 break;
1914 }
1915 String newText = verifyText (oldText, start [0], end [0], event);
1916 if (newText == null) return false;
1917 if (newText == oldText) return true;
1918 TCHAR buffer = new TCHAR (getCodePage (), newText, true);
1919 OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
1920 OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer);
1921 return false;
1922 }
1923
1924 /**
1925 * Selects the item at the given zero-relative index in the receiver's
1926 * list. If the item at the index was already selected, it remains
1927 * selected. Indices that are out of range are ignored.
1928 *
1929 * @param index the index of the item to select
1930 *
1931 * @exception SWTException <ul>
1932 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1933 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1934 * </ul>
1935 */
select(int index)1936 public void select (int index) {
1937 checkWidget ();
1938 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1939 if (0 <= index && index < count) {
1940 int selection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
1941 //corner case for single elements combo boxes for Bug 222752
1942 if (OS.WIN32_VERSION < OS.VERSION (6, 2) && getListVisible() && (style & SWT.READ_ONLY) != 0 && count==1 && selection == OS.CB_ERR) {
1943 OS.SendMessage (handle, OS.WM_KEYDOWN, OS.VK_DOWN, 0);
1944 sendEvent (SWT.Modify);
1945 return;
1946 }
1947 int code = (int)OS.SendMessage (handle, OS.CB_SETCURSEL, index, 0);
1948 if (code != OS.CB_ERR && code != selection) {
1949 //Workaround for Bug 222752
1950 if (OS.WIN32_VERSION < OS.VERSION (6, 2) && getListVisible() && (style & SWT.READ_ONLY) != 0) {
1951 int firstKey = OS.VK_UP;
1952 int secondKey = OS.VK_DOWN;
1953 if (index == 0) {
1954 firstKey = OS.VK_DOWN;
1955 secondKey = OS.VK_UP;
1956 }
1957 OS.SendMessage (handle, OS.WM_KEYDOWN, firstKey, 0);
1958 OS.SendMessage (handle, OS.WM_KEYDOWN, secondKey, 0);
1959 }
1960 sendEvent (SWT.Modify);
1961 // widget could be disposed at this point
1962 }
1963 }
1964 }
1965
1966 @Override
setBackgroundImage(long hBitmap)1967 void setBackgroundImage (long hBitmap) {
1968 super.setBackgroundImage (hBitmap);
1969 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
1970 if (hwndText != 0) OS.InvalidateRect (hwndText, null, true);
1971 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
1972 if (hwndList != 0) OS.InvalidateRect (hwndList, null, true);
1973 }
1974
1975 @Override
setBackgroundPixel(int pixel)1976 void setBackgroundPixel (int pixel) {
1977 super.setBackgroundPixel (pixel);
1978 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
1979 if (hwndText != 0) OS.InvalidateRect (hwndText, null, true);
1980 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
1981 if (hwndList != 0) OS.InvalidateRect (hwndList, null, true);
1982 }
1983
1984 @Override
setBoundsInPixels(int x, int y, int width, int height, int flags)1985 void setBoundsInPixels (int x, int y, int width, int height, int flags) {
1986 /*
1987 * Feature in Windows. If the combo box has the CBS_DROPDOWN
1988 * or CBS_DROPDOWNLIST style, Windows uses the height that the
1989 * programmer sets in SetWindowPos () to control height of the
1990 * drop down list. When the width is non-zero, Windows remembers
1991 * this value and sets the height to be the height of the text
1992 * field part of the combo box. If the width is zero, Windows
1993 * allows the height to have any value. Therefore, when the
1994 * programmer sets and then queries the height, the values can
1995 * be different depending on the width. The problem occurs when
1996 * the programmer uses computeSize () to determine the preferred
1997 * height (always the height of the text field) and then uses
1998 * this value to set the height of the combo box. The result
1999 * is a combo box with a zero size drop down list. The fix, is
2000 * to always set the height to show a fixed number of combo box
2001 * items and ignore the height value that the programmer supplies.
2002 */
2003 if ((style & SWT.DROP_DOWN) != 0) {
2004 int visibleCount = getItemCount() == 0 ? VISIBLE_COUNT : this.visibleCount;
2005 height = getTextHeightInPixels () + (getItemHeightInPixels () * visibleCount) + 2;
2006 /*
2007 * Feature in Windows. When a drop down combo box is resized,
2008 * the combo box resizes the height of the text field and uses
2009 * the height provided in SetWindowPos () to determine the height
2010 * of the drop down list. For some reason, the combo box redraws
2011 * the whole area, not just the text field. The fix is to set the
2012 * SWP_NOSIZE bits when the height of text field and the drop down
2013 * list is the same as the requested height.
2014 *
2015 * NOTE: Setting the width of a combo box to zero does not update
2016 * the width of the drop down control rect. If the width of the
2017 * combo box is zero, then do not set SWP_NOSIZE.
2018 */
2019 RECT rect = new RECT ();
2020 OS.GetWindowRect (handle, rect);
2021 if (rect.right - rect.left != 0) {
2022 if (OS.SendMessage (handle, OS.CB_GETDROPPEDCONTROLRECT, 0, rect) != 0) {
2023 int oldWidth = rect.right - rect.left, oldHeight = rect.bottom - rect.top;
2024 if (oldWidth == width && oldHeight == height) flags |= OS.SWP_NOSIZE;
2025 }
2026 }
2027 OS.SetWindowPos (handle, 0, x, y, width, height, flags);
2028 } else {
2029 super.setBoundsInPixels (x, y, width, height, flags);
2030 }
2031 }
2032
2033 @Override
setFont(Font font)2034 public void setFont (Font font) {
2035 checkWidget ();
2036
2037 /*
2038 * Feature in Windows. For some reason, in a editable combo box,
2039 * when WM_SETFONT is used to set the font of the control
2040 * and the current text does not match an item in the
2041 * list, Windows selects the item that most closely matches the
2042 * contents of the combo. The fix is to lock the current text
2043 * by ignoring all WM_SETTEXT messages during processing of
2044 * WM_SETFONT.
2045 */
2046 boolean oldLockText = lockText;
2047 if ((style & SWT.READ_ONLY) == 0) lockText = true;
2048 super.setFont (font);
2049 if ((style & SWT.READ_ONLY) == 0) lockText = oldLockText;
2050 if ((style & SWT.H_SCROLL) != 0) setScrollWidth ();
2051 }
2052
2053 @Override
setForegroundPixel(int pixel)2054 void setForegroundPixel (int pixel) {
2055 super.setForegroundPixel (pixel);
2056 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
2057 if (hwndText != 0) OS.InvalidateRect (hwndText, null, true);
2058 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
2059 if (hwndList != 0) OS.InvalidateRect (hwndList, null, true);
2060 }
2061
2062 /**
2063 * Sets the text of the item in the receiver's list at the given
2064 * zero-relative index to the string argument.
2065 *
2066 * @param index the index for the item
2067 * @param string the new text for the item
2068 *
2069 * @exception IllegalArgumentException <ul>
2070 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
2071 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
2072 * </ul>
2073 * @exception SWTException <ul>
2074 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2075 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2076 * </ul>
2077 */
setItem(int index, String string)2078 public void setItem (int index, String string) {
2079 checkWidget ();
2080 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
2081 int selection = getSelectionIndex ();
2082 remove (index, false);
2083 if (isDisposed ()) return;
2084 add (string, index);
2085 if (selection != -1) select (selection);
2086 }
2087
2088 /**
2089 * Sets the receiver's list to be the given array of items.
2090 *
2091 * @param items the array of items
2092 *
2093 * @exception IllegalArgumentException <ul>
2094 * <li>ERROR_NULL_ARGUMENT - if the items array is null</li>
2095 * <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li>
2096 * </ul>
2097 * @exception SWTException <ul>
2098 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2099 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2100 * </ul>
2101 */
setItems(String... items)2102 public void setItems (String... items) {
2103 checkWidget ();
2104 if (items == null) error (SWT.ERROR_NULL_ARGUMENT);
2105 for (String item : items) {
2106 if (item == null) error (SWT.ERROR_INVALID_ARGUMENT);
2107 }
2108 RECT rect = null;
2109 long hDC = 0, oldFont = 0, newFont = 0;
2110 int newWidth = 0;
2111 if ((style & SWT.H_SCROLL) != 0) {
2112 rect = new RECT ();
2113 hDC = OS.GetDC (handle);
2114 newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
2115 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
2116 setScrollWidth (0);
2117 }
2118 OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0);
2119 int codePage = getCodePage ();
2120 for (String item : items) {
2121 TCHAR buffer = new TCHAR (codePage, item, true);
2122 int code = (int)OS.SendMessage (handle, OS.CB_ADDSTRING, 0, buffer);
2123 if (code == OS.CB_ERR) error (SWT.ERROR_ITEM_NOT_ADDED);
2124 if (code == OS.CB_ERRSPACE) error (SWT.ERROR_ITEM_NOT_ADDED);
2125 if ((style & SWT.H_SCROLL) != 0) {
2126 int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
2127 OS.DrawText (hDC, buffer, -1, rect, flags);
2128 newWidth = Math.max (newWidth, rect.right - rect.left);
2129 }
2130 }
2131 if ((style & SWT.H_SCROLL) != 0) {
2132 if (newFont != 0) OS.SelectObject (hDC, oldFont);
2133 OS.ReleaseDC (handle, hDC);
2134 setScrollWidth (newWidth + 3);
2135 }
2136 sendEvent (SWT.Modify);
2137 // widget could be disposed at this point
2138 }
2139
2140 /**
2141 * Sets the orientation of the receiver, which must be one
2142 * of the constants <code>SWT.LEFT_TO_RIGHT</code> or <code>SWT.RIGHT_TO_LEFT</code>.
2143 * <p>
2144 *
2145 * @param orientation new orientation style
2146 *
2147 * @exception SWTException <ul>
2148 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2149 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2150 * </ul>
2151 *
2152 * @since 2.1.2
2153 */
2154 @Override
setOrientation(int orientation)2155 public void setOrientation (int orientation) {
2156 super.setOrientation (orientation);
2157 }
2158
setScrollWidth()2159 void setScrollWidth () {
2160 int newWidth = 0;
2161 RECT rect = new RECT ();
2162 long newFont, oldFont = 0;
2163 long hDC = OS.GetDC (handle);
2164 newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
2165 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
2166 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
2167 int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
2168 for (int i=0; i<count; i++) {
2169 int length = (int)OS.SendMessage (handle, OS.CB_GETLBTEXTLEN, i, 0);
2170 if (length != OS.CB_ERR) {
2171 char [] buffer = new char [length + 1];
2172 int result = (int)OS.SendMessage (handle, OS.CB_GETLBTEXT, i, buffer);
2173 if (result != OS.CB_ERR) {
2174 OS.DrawText (hDC, buffer, -1, rect, flags);
2175 newWidth = Math.max (newWidth, rect.right - rect.left);
2176 }
2177 }
2178 }
2179 if (newFont != 0) OS.SelectObject (hDC, oldFont);
2180 OS.ReleaseDC (handle, hDC);
2181 setScrollWidth (newWidth + 3);
2182 }
2183
setScrollWidth(int scrollWidth)2184 void setScrollWidth (int scrollWidth) {
2185 this.scrollWidth = scrollWidth;
2186 if ((style & SWT.SIMPLE) != 0) {
2187 OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, scrollWidth, 0);
2188 return;
2189 }
2190 boolean scroll = false;
2191 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
2192 if (count > 3) {
2193 long hmonitor = OS.MonitorFromWindow (handle, OS.MONITOR_DEFAULTTONEAREST);
2194 MONITORINFO lpmi = new MONITORINFO ();
2195 lpmi.cbSize = MONITORINFO.sizeof;
2196 OS.GetMonitorInfo (hmonitor, lpmi);
2197 int maxWidth = (lpmi.rcWork_right - lpmi.rcWork_left) / 4;
2198 scroll = scrollWidth > maxWidth;
2199 }
2200 /*
2201 * Feature in Windows. For some reason, in a editable combo box,
2202 * when CB_SETDROPPEDWIDTH is used to set the width of the drop
2203 * down list and the current text does not match an item in the
2204 * list, Windows selects the item that most closely matches the
2205 * contents of the combo. The fix is to lock the current text
2206 * by ignoring all WM_SETTEXT messages during processing of
2207 * CB_SETDROPPEDWIDTH.
2208 */
2209 boolean oldLockText = lockText;
2210 if ((style & SWT.READ_ONLY) == 0) lockText = true;
2211 if (scroll) {
2212 OS.SendMessage (handle, OS.CB_SETDROPPEDWIDTH, 0, 0);
2213 OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, scrollWidth, 0);
2214 } else {
2215 scrollWidth += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
2216 OS.SendMessage (handle, OS.CB_SETDROPPEDWIDTH, scrollWidth, 0);
2217 OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, 0, 0);
2218 }
2219 if ((style & SWT.READ_ONLY) == 0) lockText = oldLockText;
2220 }
2221
setScrollWidth(char[] buffer, boolean grow)2222 void setScrollWidth (char[] buffer, boolean grow) {
2223 RECT rect = new RECT ();
2224 long newFont, oldFont = 0;
2225 long hDC = OS.GetDC (handle);
2226 newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
2227 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
2228 int flags = OS.DT_CALCRECT | OS.DT_SINGLELINE | OS.DT_NOPREFIX;
2229 OS.DrawText (hDC, buffer, -1, rect, flags);
2230 if (newFont != 0) OS.SelectObject (hDC, oldFont);
2231 OS.ReleaseDC (handle, hDC);
2232 setScrollWidth (rect.right - rect.left, grow);
2233 }
2234
setScrollWidth(int newWidth, boolean grow)2235 void setScrollWidth (int newWidth, boolean grow) {
2236 if (grow) {
2237 if (newWidth <= scrollWidth) return;
2238 setScrollWidth (newWidth + 3);
2239 } else {
2240 if (newWidth < scrollWidth) return;
2241 setScrollWidth ();
2242 }
2243 }
2244
2245 /**
2246 * Sets the selection in the receiver's text field to the
2247 * range specified by the argument whose x coordinate is the
2248 * start of the selection and whose y coordinate is the end
2249 * of the selection.
2250 *
2251 * @param selection a point representing the new selection start and end
2252 *
2253 * @exception IllegalArgumentException <ul>
2254 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
2255 * </ul>
2256 * @exception SWTException <ul>
2257 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2258 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2259 * </ul>
2260 */
setSelection(Point selection)2261 public void setSelection (Point selection) {
2262 checkWidget ();
2263 if (selection == null) error (SWT.ERROR_NULL_ARGUMENT);
2264 int start = translateOffset (selection.x), end = translateOffset (selection.y);
2265 long bits = OS.MAKELPARAM (start, end);
2266 OS.SendMessage (handle, OS.CB_SETEDITSEL, 0, bits);
2267 }
2268
2269 /**
2270 * Sets the contents of the receiver's text field to the
2271 * given string.
2272 * <p>
2273 * This call is ignored when the receiver is read only and
2274 * the given string is not in the receiver's list.
2275 * </p>
2276 * <p>
2277 * Note: The text field in a <code>Combo</code> is typically
2278 * only capable of displaying a single line of text. Thus,
2279 * setting the text to a string containing line breaks or
2280 * other special characters will probably cause it to
2281 * display incorrectly.
2282 * </p><p>
2283 * Also note, if control characters like '\n', '\t' etc. are used
2284 * in the string, then the behavior is platform dependent.
2285 * </p>
2286 *
2287 * @param string the new text
2288 *
2289 * @exception IllegalArgumentException <ul>
2290 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
2291 * </ul>
2292 * @exception SWTException <ul>
2293 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2294 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2295 * </ul>
2296 */
setText(String string)2297 public void setText (String string) {
2298 checkWidget ();
2299 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
2300 if ((style & SWT.READ_ONLY) != 0) {
2301 int index = indexOf (string);
2302 if (index != -1) select (index);
2303 return;
2304 }
2305 clearSegments (false);
2306 int limit = LIMIT;
2307 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
2308 if (hwndText != 0) {
2309 limit = (int)OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF;
2310 }
2311 if (string.length () > limit) string = string.substring (0, limit);
2312 TCHAR buffer = new TCHAR (getCodePage (), string, true);
2313 if (OS.SetWindowText (handle, buffer)) {
2314 applyEditSegments ();
2315 sendEvent (SWT.Modify);
2316 // widget could be disposed at this point
2317 }
2318 }
2319
2320 /**
2321 * Sets the maximum number of characters that the receiver's
2322 * text field is capable of holding to be the argument.
2323 * <p>
2324 * To reset this value to the default, use <code>setTextLimit(Combo.LIMIT)</code>.
2325 * Specifying a limit value larger than <code>Combo.LIMIT</code> sets the
2326 * receiver's limit to <code>Combo.LIMIT</code>.
2327 * </p>
2328 * @param limit new text limit
2329 *
2330 * @exception IllegalArgumentException <ul>
2331 * <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
2332 * </ul>
2333 * @exception SWTException <ul>
2334 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2335 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2336 * </ul>
2337 *
2338 * @see #LIMIT
2339 */
setTextLimit(int limit)2340 public void setTextLimit (int limit) {
2341 checkWidget ();
2342 if (limit == 0) error (SWT.ERROR_CANNOT_BE_ZERO);
2343 if (segments != null && limit > 0) {
2344 OS.SendMessage (handle, OS.CB_LIMITTEXT, limit + Math.min (segments.length, LIMIT - limit), 0);
2345 } else {
2346 OS.SendMessage (handle, OS.CB_LIMITTEXT, limit, 0);
2347 }
2348 }
2349
2350 @Override
setToolTipText(Shell shell, String string)2351 void setToolTipText (Shell shell, String string) {
2352 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
2353 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
2354 if (hwndText != 0) shell.setToolTipText (hwndText, string);
2355 if (hwndList != 0) shell.setToolTipText (hwndList, string);
2356 shell.setToolTipText (handle, string);
2357 }
2358
2359 /**
2360 * Sets the number of items that are visible in the drop
2361 * down portion of the receiver's list.
2362 * <p>
2363 * Note: This operation is a hint and is not supported on
2364 * platforms that do not have this concept.
2365 * </p>
2366 *
2367 * @param count the new number of items to be visible
2368 *
2369 * @exception SWTException <ul>
2370 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
2371 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
2372 * </ul>
2373 *
2374 * @since 3.0
2375 */
setVisibleItemCount(int count)2376 public void setVisibleItemCount (int count) {
2377 checkWidget ();
2378 if (count < 0) return;
2379 visibleCount = count;
2380 updateDropDownHeight ();
2381 }
2382
2383 @Override
subclass()2384 void subclass () {
2385 super.subclass ();
2386 long newProc = display.windowProc;
2387 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
2388 if (hwndText != 0) {
2389 OS.SetWindowLongPtr (hwndText, OS.GWLP_WNDPROC, newProc);
2390 }
2391 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
2392 if (hwndList != 0) {
2393 OS.SetWindowLongPtr (hwndList, OS.GWLP_WNDPROC, newProc);
2394 }
2395 }
2396
translateOffset(int offset)2397 int translateOffset (int offset) {
2398 if (segments == null) return offset;
2399 for (int i = 0, nSegments = segments.length; i < nSegments && offset - i >= segments[i]; i++) {
2400 offset++;
2401 }
2402 return offset;
2403 }
2404
2405 @Override
translateTraversal(MSG msg)2406 boolean translateTraversal (MSG msg) {
2407 /*
2408 * When the combo box is dropped down, allow return
2409 * to select an item in the list and escape to close
2410 * the combo box.
2411 */
2412 switch ((int)(msg.wParam)) {
2413 case OS.VK_RETURN:
2414 case OS.VK_ESCAPE:
2415 if ((style & SWT.DROP_DOWN) != 0) {
2416 if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) {
2417 return false;
2418 }
2419 }
2420 }
2421 return super.translateTraversal (msg);
2422 }
2423
2424 @Override
traverseEscape()2425 boolean traverseEscape () {
2426 if ((style & SWT.DROP_DOWN) != 0) {
2427 if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) {
2428 OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, 0, 0);
2429 return true;
2430 }
2431 }
2432 return super.traverseEscape ();
2433 }
2434
2435 @Override
traverseReturn()2436 boolean traverseReturn () {
2437 if ((style & SWT.DROP_DOWN) != 0) {
2438 if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) {
2439 OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, 0, 0);
2440 return true;
2441 }
2442 }
2443 return super.traverseReturn ();
2444 }
2445
2446 @Override
unsubclass()2447 void unsubclass () {
2448 super.unsubclass ();
2449 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
2450 if (hwndText != 0 && EditProc != 0) {
2451 OS.SetWindowLongPtr (hwndText, OS.GWLP_WNDPROC, EditProc);
2452 }
2453 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
2454 if (hwndList != 0 && ListProc != 0) {
2455 OS.SetWindowLongPtr (hwndList, OS.GWLP_WNDPROC, ListProc);
2456 }
2457 }
2458
untranslateOffset(int offset)2459 int untranslateOffset (int offset) {
2460 if (segments == null) return offset;
2461 for (int i = 0, nSegments = segments.length; i < nSegments && offset > segments[i]; i++) {
2462 offset--;
2463 }
2464 return offset;
2465 }
2466
updateDropDownHeight()2467 void updateDropDownHeight () {
2468 /*
2469 * Feature in Windows. If the combo box has the CBS_DROPDOWN
2470 * or CBS_DROPDOWNLIST style, Windows uses the height that the
2471 * programmer sets in SetWindowPos () to control height of the
2472 * drop down list. See #setBounds() for more details.
2473 */
2474 if ((style & SWT.DROP_DOWN) != 0) {
2475 RECT rect = new RECT ();
2476 OS.SendMessage (handle, OS.CB_GETDROPPEDCONTROLRECT, 0, rect);
2477 int visibleCount = getItemCount() == 0 ? VISIBLE_COUNT : this.visibleCount;
2478 int height = getTextHeightInPixels () + (getItemHeightInPixels () * visibleCount) + 2;
2479 if (height != (rect.bottom - rect.top)) {
2480 forceResize ();
2481 OS.GetWindowRect (handle, rect);
2482 int flags = OS.SWP_NOMOVE | OS.SWP_NOZORDER | OS.SWP_DRAWFRAME | OS.SWP_NOACTIVATE;
2483 OS.SetWindowPos (handle, 0, 0, 0, rect.right - rect.left, height, flags);
2484 }
2485 }
2486 }
2487
2488 @Override
updateTextDirection(int textDirection)2489 boolean updateTextDirection(int textDirection) {
2490 if (super.updateTextDirection(textDirection)) {
2491 clearSegments (true);
2492 applyEditSegments ();
2493 applyListSegments ();
2494 return true;
2495 }
2496 return false;
2497 }
2498
2499 @Override
updateOrientation()2500 void updateOrientation () {
2501 int bits = OS.GetWindowLong (handle, OS.GWL_EXSTYLE);
2502 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
2503 bits |= OS.WS_EX_LAYOUTRTL;
2504 } else {
2505 bits &= ~OS.WS_EX_LAYOUTRTL;
2506 }
2507 bits &= ~OS.WS_EX_RTLREADING;
2508 OS.SetWindowLong (handle, OS.GWL_EXSTYLE, bits);
2509 long hwndText = 0, hwndList = 0;
2510 COMBOBOXINFO pcbi = new COMBOBOXINFO ();
2511 pcbi.cbSize = COMBOBOXINFO.sizeof;
2512 if (OS.GetComboBoxInfo (handle, pcbi)) {
2513 hwndText = pcbi.hwndItem;
2514 hwndList = pcbi.hwndList;
2515 }
2516 if (hwndText != 0) {
2517 int bits1 = OS.GetWindowLong (hwndText, OS.GWL_EXSTYLE);
2518 int bits2 = OS.GetWindowLong (hwndText, OS.GWL_STYLE);
2519 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
2520 bits1 |= OS.WS_EX_RIGHT | OS.WS_EX_RTLREADING;
2521 bits2 |= OS.ES_RIGHT;
2522 } else {
2523 bits1 &= ~(OS.WS_EX_RIGHT | OS.WS_EX_RTLREADING);
2524 bits2 &= ~OS.ES_RIGHT;
2525 }
2526 OS.SetWindowLong (hwndText, OS.GWL_EXSTYLE, bits1);
2527 OS.SetWindowLong (hwndText, OS.GWL_STYLE, bits2);
2528
2529 /*
2530 * Bug in Windows. For some reason, the single line text field
2531 * portion of the combo box does not redraw to reflect the new
2532 * style bits. The fix is to force the widget to be resized by
2533 * temporarily shrinking and then growing the width and height.
2534 */
2535 RECT rect = new RECT ();
2536 OS.GetWindowRect (hwndText, rect);
2537 int width = rect.right - rect.left, height = rect.bottom - rect.top;
2538 OS.GetWindowRect (handle, rect);
2539 int widthCombo = rect.right - rect.left, heightCombo = rect.bottom - rect.top;
2540 int uFlags = OS.SWP_NOMOVE | OS.SWP_NOZORDER | OS.SWP_NOACTIVATE;
2541 OS.SetWindowPos (hwndText, 0, 0, 0, width - 1, height - 1, uFlags);
2542 OS.SetWindowPos (handle, 0, 0, 0, widthCombo - 1, heightCombo - 1, uFlags);
2543 OS.SetWindowPos (hwndText, 0, 0, 0, width, height, uFlags);
2544 OS.SetWindowPos (handle, 0, 0, 0, widthCombo, heightCombo, uFlags);
2545 OS.InvalidateRect (handle, null, true);
2546 }
2547 if (hwndList != 0) {
2548 int bits1 = OS.GetWindowLong (hwndList, OS.GWL_EXSTYLE);
2549 if ((style & SWT.RIGHT_TO_LEFT) != 0) {
2550 bits1 |= OS.WS_EX_LAYOUTRTL;
2551 } else {
2552 bits1 &= ~OS.WS_EX_LAYOUTRTL;
2553 }
2554 OS.SetWindowLong (hwndList, OS.GWL_EXSTYLE, bits1);
2555 }
2556 }
2557
verifyText(String string, int start, int end, Event keyEvent)2558 String verifyText (String string, int start, int end, Event keyEvent) {
2559 Event event = new Event ();
2560 event.text = string;
2561 event.start = start;
2562 event.end = end;
2563 if (keyEvent != null) {
2564 event.character = keyEvent.character;
2565 event.keyCode = keyEvent.keyCode;
2566 event.stateMask = keyEvent.stateMask;
2567 }
2568 event.start = untranslateOffset (event.start);
2569 event.end = untranslateOffset (event.end);
2570 /*
2571 * It is possible (but unlikely), that application
2572 * code could have disposed the widget in the verify
2573 * event. If this happens, answer null to cancel
2574 * the operation.
2575 */
2576 sendEvent (SWT.Verify, event);
2577 if (!event.doit || isDisposed ()) return null;
2578 return event.text;
2579 }
2580
2581 @Override
widgetExtStyle()2582 int widgetExtStyle () {
2583 return super.widgetExtStyle () & ~OS.WS_EX_NOINHERITLAYOUT;
2584 }
2585
2586 @Override
widgetStyle()2587 int widgetStyle () {
2588 int bits = super.widgetStyle () | OS.CBS_AUTOHSCROLL | OS.CBS_NOINTEGRALHEIGHT | OS.WS_HSCROLL |OS.WS_VSCROLL;
2589 if ((style & SWT.SIMPLE) != 0) return bits | OS.CBS_SIMPLE;
2590 if ((style & SWT.READ_ONLY) != 0) return bits | OS.CBS_DROPDOWNLIST;
2591 return bits | OS.CBS_DROPDOWN;
2592 }
2593
2594 @Override
windowClass()2595 TCHAR windowClass () {
2596 return ComboClass;
2597 }
2598
2599 @Override
windowProc()2600 long windowProc () {
2601 return ComboProc;
2602 }
2603
2604 @Override
windowProc(long hwnd, int msg, long wParam, long lParam)2605 long windowProc (long hwnd, int msg, long wParam, long lParam) {
2606 if (handle == 0) return 0;
2607 if (hwnd != handle) {
2608 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
2609 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
2610 if ((hwndText != 0 && hwnd == hwndText) || (hwndList != 0 && hwnd == hwndList)) {
2611 LRESULT result = null;
2612 boolean processSegments = false, redraw = false;
2613 switch (msg) {
2614 /* Keyboard messages */
2615 case OS.WM_CHAR:
2616 processSegments = (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) && !ignoreCharacter && OS.GetKeyState (OS.VK_CONTROL) >= 0 && OS.GetKeyState (OS.VK_MENU) >= 0;
2617 result = wmChar (hwnd, wParam, lParam);
2618 break;
2619 case OS.WM_IME_CHAR: result = wmIMEChar (hwnd, wParam, lParam); break;
2620 case OS.WM_KEYDOWN:
2621 processSegments = wParam == OS.VK_DELETE && (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0));
2622 result = wmKeyDown (hwnd, wParam, lParam);
2623 break;
2624 case OS.WM_KEYUP: result = wmKeyUp (hwnd, wParam, lParam); break;
2625 case OS.WM_SYSCHAR: result = wmSysChar (hwnd, wParam, lParam); break;
2626 case OS.WM_SYSKEYDOWN: result = wmSysKeyDown (hwnd, wParam, lParam); break;
2627 case OS.WM_SYSKEYUP: result = wmSysKeyUp (hwnd, wParam, lParam); break;
2628
2629 /* Mouse Messages */
2630 case OS.WM_CAPTURECHANGED: result = wmCaptureChanged (hwnd, wParam, lParam); break;
2631 case OS.WM_LBUTTONDBLCLK: result = wmLButtonDblClk (hwnd, wParam, lParam); break;
2632 case OS.WM_LBUTTONDOWN: result = wmLButtonDown (hwnd, wParam, lParam); break;
2633 case OS.WM_LBUTTONUP: result = wmLButtonUp (hwnd, wParam, lParam); break;
2634 case OS.WM_MBUTTONDBLCLK: result = wmMButtonDblClk (hwnd, wParam, lParam); break;
2635 case OS.WM_MBUTTONDOWN: result = wmMButtonDown (hwnd, wParam, lParam); break;
2636 case OS.WM_MBUTTONUP: result = wmMButtonUp (hwnd, wParam, lParam); break;
2637 case OS.WM_MOUSEHOVER: result = wmMouseHover (hwnd, wParam, lParam); break;
2638 case OS.WM_MOUSELEAVE: result = wmMouseLeave (hwnd, wParam, lParam); break;
2639 case OS.WM_MOUSEMOVE: result = wmMouseMove (hwnd, wParam, lParam); break;
2640 // case OS.WM_MOUSEWHEEL: result = wmMouseWheel (hwnd, wParam, lParam); break;
2641 case OS.WM_RBUTTONDBLCLK: result = wmRButtonDblClk (hwnd, wParam, lParam); break;
2642 case OS.WM_RBUTTONDOWN: result = wmRButtonDown (hwnd, wParam, lParam); break;
2643 case OS.WM_RBUTTONUP: result = wmRButtonUp (hwnd, wParam, lParam); break;
2644 case OS.WM_XBUTTONDBLCLK: result = wmXButtonDblClk (hwnd, wParam, lParam); break;
2645 case OS.WM_XBUTTONDOWN: result = wmXButtonDown (hwnd, wParam, lParam); break;
2646 case OS.WM_XBUTTONUP: result = wmXButtonUp (hwnd, wParam, lParam); break;
2647
2648 /* Paint messages */
2649 case OS.WM_PAINT: result = wmPaint (hwnd, wParam, lParam); break;
2650
2651 /* Menu messages */
2652 case OS.WM_CONTEXTMENU: result = wmContextMenu (hwnd, wParam, lParam); break;
2653
2654 /* Clipboard messages */
2655 case OS.EM_CANUNDO:
2656 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) return 0;
2657 break;
2658 case OS.WM_UNDO:
2659 case OS.EM_UNDO:
2660 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) return 0;
2661 case OS.WM_COPY:
2662 case OS.WM_CLEAR:
2663 case OS.WM_CUT:
2664 case OS.WM_PASTE:
2665 processSegments = hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0);
2666 case OS.WM_SETTEXT:
2667 if (hwnd == hwndText) {
2668 result = wmClipboard (hwnd, msg, wParam, lParam);
2669 }
2670 break;
2671 }
2672 if (result != null) return result.value;
2673
2674 if (processSegments) {
2675 if (getDrawing () && OS.IsWindowVisible (hwndText)) {
2676 redraw = true;
2677 OS.DefWindowProc (hwndText, OS.WM_SETREDRAW, 0, 0);
2678 }
2679 clearSegments (true);
2680 long code = callWindowProc (hwnd, msg, wParam, lParam);
2681 applyEditSegments ();
2682 if (redraw) {
2683 OS.DefWindowProc (hwndText, OS.WM_SETREDRAW, 1, 0);
2684 OS.InvalidateRect (hwndText, null, true);
2685 }
2686 return code;
2687 }
2688 return callWindowProc (hwnd, msg, wParam, lParam);
2689 }
2690 }
2691 switch (msg) {
2692 case OS.CB_SETCURSEL: {
2693 long code = OS.CB_ERR;
2694 int index = (int) wParam;
2695 if ((style & SWT.READ_ONLY) != 0) {
2696 if (hooks (SWT.Verify) || filters (SWT.Verify)) {
2697 String oldText = getText (), newText = null;
2698 if (wParam == -1) {
2699 newText = "";
2700 } else {
2701 if (0 <= wParam && wParam < getItemCount ()) {
2702 newText = getItem ((int)wParam);
2703 }
2704 }
2705 if (newText != null && !newText.equals (oldText)) {
2706 int length = OS.GetWindowTextLength (handle);
2707 oldText = newText;
2708 newText = verifyText (newText, 0, length, null);
2709 if (newText == null) return 0;
2710 if (!newText.equals (oldText)) {
2711 index = indexOf (newText);
2712 if (index != -1 && index != wParam) {
2713 return callWindowProc (handle, OS.CB_SETCURSEL, index, lParam);
2714 }
2715 }
2716 }
2717 }
2718 }
2719 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) {
2720 code = super.windowProc (hwnd, msg, wParam, lParam);
2721 if (!(code == OS.CB_ERR || code == OS.CB_ERRSPACE)) {
2722 Event event = getSegments (items [index]);
2723 segments = event != null ? event.segments : null;
2724 if (event.segmentsChars != null) {
2725 int auto = state & HAS_AUTO_DIRECTION;
2726 if (event.segmentsChars[0] == RLE) {
2727 super.updateTextDirection(SWT.RIGHT_TO_LEFT);
2728 } else if (event.segmentsChars[0] == LRE) {
2729 super.updateTextDirection(SWT.LEFT_TO_RIGHT);
2730 }
2731 state |= auto;
2732 }
2733 return code;
2734 }
2735 }
2736 break;
2737 }
2738 case OS.CB_ADDSTRING:
2739 case OS.CB_INSERTSTRING:
2740 case OS.CB_FINDSTRINGEXACT:
2741 if (lParam != 0 && (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0))) {
2742 long code = OS.CB_ERR;
2743 int length = OS.wcslen (lParam);
2744 TCHAR buffer = new TCHAR (getCodePage (), length);
2745 OS.MoveMemory (buffer, lParam, buffer.length () * TCHAR.sizeof);
2746 String string = buffer.toString (0, length);
2747 Event event = getSegments (string);
2748 if (event != null && event.segments != null) {
2749 buffer = new TCHAR (getCodePage (), getSegmentsText (string, event), true);
2750 long hHeap = OS.GetProcessHeap ();
2751 length = buffer.length() * TCHAR.sizeof;
2752 long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, length);
2753 OS.MoveMemory (pszText, buffer, length);
2754 code = super.windowProc (hwnd, msg, wParam, pszText);
2755 OS.HeapFree (hHeap, 0, pszText);
2756 }
2757 if (msg == OS.CB_ADDSTRING || msg == OS.CB_INSERTSTRING) {
2758 int index = msg == OS.CB_ADDSTRING ? items.length : (int) wParam;
2759 String [] newItems = new String [items.length + 1];
2760 System.arraycopy (items, 0, newItems, 0, index);
2761 newItems [index] = string;
2762 System.arraycopy (items, index, newItems, index + 1, items.length - index);
2763 items = newItems;
2764 }
2765 if (code != OS.CB_ERR && code != OS.CB_ERRSPACE) return code;
2766 }
2767 break;
2768 case OS.CB_DELETESTRING: {
2769 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) {
2770 long code = super.windowProc (hwnd, msg, wParam, lParam);
2771 if (code != OS.CB_ERR && code != OS.CB_ERRSPACE) {
2772 int index = (int) wParam;
2773 if (items.length == 1) {
2774 items = new String[0];
2775 } else if (items.length > 1) {
2776 String [] newItems = new String [items.length - 1];
2777 System.arraycopy (items, 0, newItems, 0, index);
2778 System.arraycopy (items, index + 1, newItems, index, items.length - index - 1);
2779 items = newItems;
2780 }
2781 if (!noSelection) {
2782 index = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
2783 if (index == wParam) {
2784 clearSegments (false);
2785 applyEditSegments ();
2786 }
2787 }
2788 }
2789 return code;
2790 }
2791 break;
2792 }
2793 case OS.CB_RESETCONTENT: {
2794 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) {
2795 if (items.length > 0) items = new String [0];
2796 clearSegments (false);
2797 applyEditSegments ();
2798 }
2799 break;
2800 }
2801 }
2802 return super.windowProc (hwnd, msg, wParam, lParam);
2803 }
2804
2805 @Override
wmColorChild(long wParam, long lParam)2806 LRESULT wmColorChild (long wParam, long lParam) {
2807 LRESULT result = super.wmColorChild(wParam, lParam);
2808
2809 /*
2810 * CBS_DROPDOWNLIST (SWT.READ_ONLY) comboboxes ignore results of WM_CTLCOLORxxx.
2811 * This prevents SWT from setting custom background / text color.
2812 * In Windows function 'comctl32!ComboBox_InternalUpdateEditWindow' there are two main branches:
2813 * 'DrawThemeText' branch
2814 * Ignores any SetTextColor / SetBkColor.
2815 * Ignores brush returned from WM_CTLCOLORxxx.
2816 * Keeps any background that was painted during WM_CTLCOLORxxx.
2817 * 'ExtTextOut' branch
2818 * Uses pre-selected SetTextColor / SetBkColor.
2819 * Ignores brush returned from WM_CTLCOLORxxx.
2820 * Overwrites background with color in SetBkColor.
2821 * This undocumented hack forces combobox to use 'ExtTextOut' branch.
2822 * The flag is reset after every WM_PAINT, so it's set in every WM_CTLCOLORxxx.
2823 * Since 'ExtTextOut' always paints background, hack is not activated if not needed
2824 * to avoid changes in visual appearance of comboboxes with default colors.
2825 */
2826 final boolean isReadonly = ((style & SWT.READ_ONLY) != 0);
2827 final boolean isCustomColors = (result != null);
2828 if (isReadonly && isCustomColors && stateFlagsUsable) {
2829 stateFlagsAdd(stateFlagsFirstPaint);
2830 }
2831
2832 return result;
2833 }
2834
2835 @Override
WM_CTLCOLOR(long wParam, long lParam)2836 LRESULT WM_CTLCOLOR (long wParam, long lParam) {
2837 return wmColorChild (wParam, lParam);
2838 }
2839
2840 @Override
WM_GETDLGCODE(long wParam, long lParam)2841 LRESULT WM_GETDLGCODE (long wParam, long lParam) {
2842 long code = callWindowProc (handle, OS.WM_GETDLGCODE, wParam, lParam);
2843 return new LRESULT (code | OS.DLGC_WANTARROWS);
2844 }
2845
2846 @Override
WM_KILLFOCUS(long wParam, long lParam)2847 LRESULT WM_KILLFOCUS (long wParam, long lParam) {
2848 /*
2849 * Return NULL - Focus notification is
2850 * done in WM_COMMAND by CBN_KILLFOCUS.
2851 */
2852 return null;
2853 }
2854
2855 @Override
WM_LBUTTONDOWN(long wParam, long lParam)2856 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
2857 /*
2858 * Feature in Windows. When an editable combo box is dropped
2859 * down and the text in the entry field partially matches an
2860 * item in the list, Windows selects the item but doesn't send
2861 * WM_COMMAND with CBN_SELCHANGE. The fix is to detect that
2862 * the selection has changed and issue the notification.
2863 */
2864 int oldSelection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
2865 LRESULT result = super.WM_LBUTTONDOWN (wParam, lParam);
2866 if (result == LRESULT.ZERO) return result;
2867 if ((style & SWT.READ_ONLY) == 0) {
2868 int newSelection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
2869 if (oldSelection != newSelection) {
2870 sendEvent (SWT.Modify);
2871 if (isDisposed ()) return LRESULT.ZERO;
2872 sendSelectionEvent (SWT.Selection, null, true);
2873 if (isDisposed ()) return LRESULT.ZERO;
2874 }
2875 }
2876 return result;
2877 }
2878
2879 @Override
WM_SETFOCUS(long wParam, long lParam)2880 LRESULT WM_SETFOCUS (long wParam, long lParam) {
2881 /*
2882 * Return NULL - Focus notification is
2883 * done by WM_COMMAND with CBN_SETFOCUS.
2884 */
2885 return null;
2886 }
2887
2888 @Override
WM_SIZE(long wParam, long lParam)2889 LRESULT WM_SIZE (long wParam, long lParam) {
2890 /*
2891 * Feature in Windows. When a combo box is resized,
2892 * the size of the drop down rectangle is specified
2893 * using the height and then the combo box resizes
2894 * to be the height of the text field. This causes
2895 * two WM_SIZE messages to be sent and two SWT.Resize
2896 * events to be issued. The fix is to ignore the
2897 * second resize.
2898 */
2899 if (ignoreResize) return null;
2900 /*
2901 * Bug in Windows. If the combo box has the CBS_SIMPLE style,
2902 * the list portion of the combo box is not redrawn when the
2903 * combo box is resized. The fix is to force a redraw when
2904 * the size has changed.
2905 */
2906 if ((style & SWT.SIMPLE) != 0) {
2907 LRESULT result = super.WM_SIZE (wParam, lParam);
2908 if (OS.IsWindowVisible (handle)) {
2909 int uFlags = OS.RDW_ERASE | OS.RDW_INVALIDATE | OS.RDW_ALLCHILDREN;
2910 OS.RedrawWindow (handle, null, 0, uFlags);
2911 }
2912 return result;
2913 }
2914
2915 /*
2916 * Feature in Windows. When an editable drop down combo box
2917 * contains text that does not correspond to an item in the
2918 * list, when the widget is resized, it selects the closest
2919 * match from the list. The fix is to lock the current text
2920 * by ignoring all WM_SETTEXT messages during processing of
2921 * WM_SIZE.
2922 */
2923 boolean oldLockText = lockText;
2924 if ((style & SWT.READ_ONLY) == 0) lockText = true;
2925 LRESULT result = super.WM_SIZE (wParam, lParam);
2926 if ((style & SWT.READ_ONLY) == 0) lockText = oldLockText;
2927 /*
2928 * Feature in Windows. When CB_SETDROPPEDWIDTH is called with
2929 * a width that is smaller than the current size of the combo
2930 * box, it is ignored. This the fix is to set the width after
2931 * the combo box has been resized.
2932 */
2933 if ((style & SWT.H_SCROLL) != 0) setScrollWidth (scrollWidth);
2934
2935 /*
2936 * When setting selection, Combo automatically scrolls selection's end into view.
2937 * We force it to do such scrolling after every resize to achieve multiple goals:
2938 * 1) Text is no longer partially shown when there's free space after resizing
2939 * Without workaround, this happens when all of these are true:
2940 * a) Combo has focus
2941 * b) Combo can't fit all text
2942 * c) Caret is not at position 0
2943 * d) Combo is resized bigger.
2944 * 2) Text is no longer partially shown after .setSelection() before its size was calculated
2945 * This is just another form of problem 1.
2946 * 3) Caret no longer goes out of view when shrinking control.
2947 */
2948 if ((style & SWT.READ_ONLY) == 0) {
2949 Point oldSelection = this.getSelection();
2950 Point tmpSelection = new Point(0, 0);
2951 if (!oldSelection.equals(tmpSelection)) {
2952 this.setSelection(tmpSelection);
2953 this.setSelection(oldSelection);
2954 }
2955 }
2956
2957 return result;
2958 }
2959
2960 @Override
WM_UPDATEUISTATE(long wParam, long lParam)2961 LRESULT WM_UPDATEUISTATE (long wParam, long lParam) {
2962 LRESULT result = super.WM_UPDATEUISTATE (wParam, lParam);
2963 if (result != null) return result;
2964 OS.InvalidateRect (handle, null, true);
2965 return result;
2966 }
2967
2968 @Override
WM_WINDOWPOSCHANGING(long wParam, long lParam)2969 LRESULT WM_WINDOWPOSCHANGING (long wParam, long lParam) {
2970 LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
2971 if (result != null) return result;
2972 /*
2973 * Feature in Windows. When a combo box is resized,
2974 * the size of the drop down rectangle is specified
2975 * using the height and then the combo box resizes
2976 * to be the height of the text field. This causes
2977 * sibling windows that intersect with the original
2978 * bounds to redrawn. The fix is to stop the redraw
2979 * using SWP_NOREDRAW and then damage the combo box
2980 * text field and the area in the parent where the
2981 * combo box used to be.
2982 */
2983 if (!getDrawing ()) return result;
2984 if (!OS.IsWindowVisible (handle)) return result;
2985 if (ignoreResize) {
2986 WINDOWPOS lpwp = new WINDOWPOS ();
2987 OS.MoveMemory (lpwp, lParam, WINDOWPOS.sizeof);
2988 if ((lpwp.flags & OS.SWP_NOSIZE) == 0) {
2989 lpwp.flags |= OS.SWP_NOREDRAW;
2990 OS.MoveMemory (lParam, lpwp, WINDOWPOS.sizeof);
2991 OS.InvalidateRect (handle, null, true);
2992 RECT rect = new RECT ();
2993 OS.GetWindowRect (handle, rect);
2994 int width = rect.right - rect.left;
2995 int height = rect.bottom - rect.top;
2996 if (width != 0 && height != 0) {
2997 long hwndParent = parent.handle;
2998 long hwndChild = OS.GetWindow (hwndParent, OS.GW_CHILD);
2999 OS.MapWindowPoints (0, hwndParent, rect, 2);
3000 long rgn1 = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom);
3001 while (hwndChild != 0) {
3002 if (hwndChild != handle) {
3003 OS.GetWindowRect (hwndChild, rect);
3004 OS.MapWindowPoints (0, hwndParent, rect, 2);
3005 long rgn2 = OS.CreateRectRgn (rect.left, rect.top, rect.right, rect.bottom);
3006 OS.CombineRgn (rgn1, rgn1, rgn2, OS.RGN_DIFF);
3007 OS.DeleteObject (rgn2);
3008 }
3009 hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
3010 }
3011 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
3012 OS.RedrawWindow (hwndParent, null, rgn1, flags);
3013 OS.DeleteObject (rgn1);
3014 }
3015 }
3016 }
3017 return result;
3018 }
3019
3020 @Override
wmChar(long hwnd, long wParam, long lParam)3021 LRESULT wmChar (long hwnd, long wParam, long lParam) {
3022 if (ignoreCharacter) return null;
3023 LRESULT result = super.wmChar (hwnd, wParam, lParam);
3024 if (result != null) return result;
3025 switch ((int)wParam) {
3026 /*
3027 * Feature in Windows. For some reason, when the
3028 * widget is a single line text widget, when the
3029 * user presses tab, return or escape, Windows beeps.
3030 * The fix is to look for these keys and not call
3031 * the window proc.
3032 *
3033 * NOTE: This only happens when the drop down list
3034 * is not visible.
3035 */
3036 case SWT.TAB: return LRESULT.ZERO;
3037 case SWT.CR:
3038 if (!ignoreDefaultSelection) sendSelectionEvent (SWT.DefaultSelection);
3039 ignoreDefaultSelection = false;
3040 // when no value is selected in the dropdown
3041 if (getSelectionIndex() == -1) {
3042 if ((style & SWT.DROP_DOWN) != 0 && (style & SWT.READ_ONLY) == 0) {
3043 // close the dropdown if open
3044 if (OS.SendMessage(handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) {
3045 OS.SendMessage(handle, OS.CB_SHOWDROPDOWN, 0, 0);
3046 }
3047 return LRESULT.ZERO;
3048 }
3049 }
3050 case SWT.ESC:
3051 if ((style & SWT.DROP_DOWN) != 0) {
3052 if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) == 0) {
3053 return LRESULT.ZERO;
3054 }
3055 }
3056 /*
3057 * Bug in Windows. When the user types CTRL and BS
3058 * in a combo control, a DEL character (0x08) is generated.
3059 * Rather than deleting the text, the DEL character
3060 * is inserted into the control. The fix is to detect
3061 * this case and not call the window proc.
3062 */
3063 case SWT.DEL:
3064 if (OS.GetKeyState (OS.VK_CONTROL) < 0) {
3065 if ((style & SWT.READ_ONLY) != 0) return LRESULT.ZERO;
3066 Point selection = getSelection ();
3067 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
3068 int x = selection.x;
3069 int y = selection.y;
3070 if (x == y) {
3071 String actText = getText ().substring (0, x);
3072 java.util.regex.Matcher m = CTRL_BS_PATTERN.matcher (actText);
3073 if (m.find ()) {
3074 x = m.start ();
3075 y = m.end ();
3076 OS.SendMessage (hwndText, OS.EM_SETSEL, x, y);
3077 }
3078 }
3079 if (x < y) {
3080 /*
3081 * Instead of setting the new text directly we send the replace selection event to
3082 * guarantee that the action is pushed to the undo buffer.
3083 */
3084 OS.SendMessage (hwndText, OS.EM_REPLACESEL, 1, 0);
3085 }
3086 return LRESULT.ZERO;
3087 }
3088 }
3089 return result;
3090 }
3091
wmClipboard(long hwndText, int msg, long wParam, long lParam)3092 LRESULT wmClipboard (long hwndText, int msg, long wParam, long lParam) {
3093 if ((style & SWT.READ_ONLY) != 0) return null;
3094 if (!hooks (SWT.Verify) && !filters (SWT.Verify)) return null;
3095 boolean call = false;
3096 int [] start = new int [1], end = new int [1];
3097 String newText = null;
3098 switch (msg) {
3099 case OS.WM_CLEAR:
3100 case OS.WM_CUT:
3101 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
3102 if (untranslateOffset (start [0]) != untranslateOffset (end [0])) {
3103 newText = "";
3104 call = true;
3105 }
3106 break;
3107 case OS.WM_PASTE:
3108 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
3109 newText = getClipboardText ();
3110 break;
3111 case OS.EM_UNDO:
3112 case OS.WM_UNDO:
3113 if (OS.SendMessage (hwndText, OS.EM_CANUNDO, 0, 0) != 0) {
3114 ignoreModify = true;
3115 OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
3116 int length = OS.GetWindowTextLength (hwndText);
3117 int [] newStart = new int [1], newEnd = new int [1];
3118 OS.SendMessage (hwndText, OS.EM_GETSEL, newStart, newEnd);
3119 if (length != 0 && newStart [0] != newEnd [0]) {
3120 char [] buffer = new char [length + 1];
3121 OS.GetWindowText (hwndText, buffer, length + 1);
3122 newText = new String (buffer, newStart [0], newEnd [0] - newStart [0]);
3123 } else {
3124 newText = "";
3125 }
3126 OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
3127 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
3128 ignoreModify = false;
3129 }
3130 break;
3131 case OS.WM_SETTEXT:
3132 if (lockText) return null;
3133 end [0] = OS.GetWindowTextLength (hwndText);
3134 int length = OS.wcslen (lParam);
3135 TCHAR buffer = new TCHAR (getCodePage (), length);
3136 int byteCount = buffer.length () * TCHAR.sizeof;
3137 OS.MoveMemory (buffer, lParam, byteCount);
3138 newText = buffer.toString (0, length);
3139 break;
3140 }
3141 if (newText != null) {
3142 String oldText = newText;
3143 newText = verifyText (newText, start [0], end [0], null);
3144 if (newText == null) return LRESULT.ZERO;
3145 if (!newText.equals (oldText)) {
3146 if (call) {
3147 OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
3148 }
3149 TCHAR buffer = new TCHAR (getCodePage (), newText, true);
3150 if (msg == OS.WM_SETTEXT) {
3151 long hHeap = OS.GetProcessHeap ();
3152 int byteCount = buffer.length () * TCHAR.sizeof;
3153 long pszText = OS.HeapAlloc (hHeap, OS.HEAP_ZERO_MEMORY, byteCount);
3154 OS.MoveMemory (pszText, buffer, byteCount);
3155 long code = OS.CallWindowProc (EditProc, hwndText, msg, wParam, pszText);
3156 OS.HeapFree (hHeap, 0, pszText);
3157 return new LRESULT (code);
3158 } else {
3159 OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer);
3160 return LRESULT.ZERO;
3161 }
3162 }
3163 }
3164 return null;
3165 }
3166
3167 @Override
wmCommandChild(long wParam, long lParam)3168 LRESULT wmCommandChild (long wParam, long lParam) {
3169 int code = OS.HIWORD (wParam);
3170 switch (code) {
3171 case OS.CBN_EDITCHANGE:
3172 if (ignoreModify) break;
3173 /*
3174 * Feature in Windows. If the combo box list selection is
3175 * queried using CB_GETCURSEL before the WM_COMMAND (with
3176 * CBN_EDITCHANGE) returns, CB_GETCURSEL returns the previous
3177 * selection in the list. It seems that the combo box sends
3178 * the WM_COMMAND before it makes the selection in the list box
3179 * match the entry field. The fix is remember that no selection
3180 * in the list should exist in this case.
3181 */
3182 noSelection = true;
3183 sendEvent (SWT.Modify);
3184 if (isDisposed ()) return LRESULT.ZERO;
3185 noSelection = false;
3186 break;
3187 case OS.CBN_SELCHANGE:
3188 /*
3189 * Feature in Windows. If the text in an editable combo box
3190 * is queried using GetWindowText () before the WM_COMMAND
3191 * (with CBN_SELCHANGE) returns, GetWindowText () returns is
3192 * the previous text in the combo box. It seems that the combo
3193 * box sends the WM_COMMAND before it updates the text field to
3194 * match the list selection. The fix is to force the text field
3195 * to match the list selection by re-selecting the list item.
3196 */
3197 int index = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
3198 if (index != OS.CB_ERR) {
3199 OS.SendMessage (handle, OS.CB_SETCURSEL, index, 0);
3200 }
3201 /*
3202 * It is possible (but unlikely), that application
3203 * code could have disposed the widget in the modify
3204 * event. If this happens, end the processing of the
3205 * Windows message by returning zero as the result of
3206 * the window proc.
3207 */
3208 sendEvent (SWT.Modify);
3209 if (isDisposed ()) return LRESULT.ZERO;
3210 sendSelectionEvent (SWT.Selection);
3211 break;
3212 case OS.CBN_SETFOCUS:
3213 sendFocusEvent (SWT.FocusIn);
3214 if (isDisposed ()) return LRESULT.ZERO;
3215 break;
3216 case OS.CBN_DROPDOWN:
3217 setCursor ();
3218 updateDropDownHeight ();
3219 break;
3220 case OS.CBN_KILLFOCUS:
3221 sendFocusEvent (SWT.FocusOut);
3222 if (isDisposed ()) return LRESULT.ZERO;
3223 break;
3224 case OS.EN_ALIGN_LTR_EC:
3225 case OS.EN_ALIGN_RTL_EC:
3226 Event event = new Event ();
3227 event.doit = true;
3228 sendEvent (SWT.OrientationChange, event);
3229 if (!event.doit) {
3230 long hwnd = lParam;
3231 int bits1 = OS.GetWindowLong (hwnd, OS.GWL_EXSTYLE);
3232 int bits2 = OS.GetWindowLong (hwnd, OS.GWL_STYLE);
3233 if (code == OS.EN_ALIGN_LTR_EC) {
3234 bits1 |= (OS.WS_EX_RTLREADING | OS.WS_EX_RIGHT);
3235 bits2 |= OS.ES_RIGHT;
3236 } else {
3237 bits1 &= ~(OS.WS_EX_RTLREADING | OS.WS_EX_RIGHT);
3238 bits2 &= ~OS.ES_RIGHT;
3239 }
3240 OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits1);
3241 OS.SetWindowLong (hwnd, OS.GWL_STYLE, bits2);
3242 }
3243 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) {
3244 clearSegments(true);
3245 /*
3246 * Explicit LTR or RTL direction was set, so auto direction
3247 * should be deactivated.
3248 */
3249 state &= ~HAS_AUTO_DIRECTION;
3250 applyEditSegments();
3251 }
3252 break;
3253 }
3254 return super.wmCommandChild (wParam, lParam);
3255 }
3256
3257 @Override
wmIMEChar(long hwnd, long wParam, long lParam)3258 LRESULT wmIMEChar (long hwnd, long wParam, long lParam) {
3259
3260 /* Process a DBCS character */
3261 Display display = this.display;
3262 display.lastKey = 0;
3263 display.lastAscii = (int)wParam;
3264 display.lastVirtual = display.lastNull = display.lastDead = false;
3265 if (!sendKeyEvent (SWT.KeyDown, OS.WM_IME_CHAR, wParam, lParam)) {
3266 return LRESULT.ZERO;
3267 }
3268
3269 /*
3270 * Feature in Windows. The Windows text widget uses
3271 * two 2 WM_CHAR's to process a DBCS key instead of
3272 * using WM_IME_CHAR. The fix is to allow the text
3273 * widget to get the WM_CHAR's but ignore sending
3274 * them to the application.
3275 */
3276 ignoreCharacter = true;
3277 long result = callWindowProc (hwnd, OS.WM_IME_CHAR, wParam, lParam);
3278 MSG msg = new MSG ();
3279 int flags = OS.PM_REMOVE | OS.PM_NOYIELD | OS.PM_QS_INPUT | OS.PM_QS_POSTMESSAGE;
3280 while (OS.PeekMessage (msg, hwnd, OS.WM_CHAR, OS.WM_CHAR, flags)) {
3281 OS.TranslateMessage (msg);
3282 OS.DispatchMessage (msg);
3283 }
3284 ignoreCharacter = false;
3285
3286 sendKeyEvent (SWT.KeyUp, OS.WM_IME_CHAR, wParam, lParam);
3287 // widget could be disposed at this point
3288 display.lastKey = display.lastAscii = 0;
3289 return new LRESULT (result);
3290 }
3291
3292 @Override
wmKeyDown(long hwnd, long wParam, long lParam)3293 LRESULT wmKeyDown (long hwnd, long wParam, long lParam) {
3294 if (ignoreCharacter) return null;
3295 LRESULT result = super.wmKeyDown (hwnd, wParam, lParam);
3296 if (result != null) return result;
3297 ignoreDefaultSelection = false;
3298 switch ((int)wParam) {
3299 case OS.VK_LEFT:
3300 case OS.VK_UP:
3301 case OS.VK_RIGHT:
3302 case OS.VK_DOWN:
3303 if (segments != null) {
3304 long code = 0;
3305 int [] start = new int [1], end = new int [1], newStart = new int [1], newEnd = new int [1];
3306 OS.SendMessage (handle, OS.CB_GETEDITSEL, start, end);
3307 while (true) {
3308 code = callWindowProc (hwnd, OS.WM_KEYDOWN, wParam, lParam);
3309 OS.SendMessage (handle, OS.CB_GETEDITSEL, newStart, newEnd);
3310 if (newStart [0] != start [0]) {
3311 if (untranslateOffset (newStart [0]) != untranslateOffset (start [0])) break;
3312 } else if (newEnd [0] != end [0]) {
3313 if (untranslateOffset (newEnd [0]) != untranslateOffset (end [0])) break;
3314 } else {
3315 break;
3316 }
3317 start [0] = newStart [0];
3318 end [0] = newEnd [0];
3319 }
3320 result = code == 0 ? LRESULT.ZERO : new LRESULT (code);
3321 }
3322 break;
3323 case OS.VK_RETURN:
3324 if ((style & SWT.DROP_DOWN) != 0) {
3325 if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) {
3326 ignoreDefaultSelection = true;
3327 }
3328 }
3329 break;
3330 }
3331 return result;
3332 }
3333
3334 @Override
wmSysKeyDown(long hwnd, long wParam, long lParam)3335 LRESULT wmSysKeyDown (long hwnd, long wParam, long lParam) {
3336 /*
3337 * Feature in Windows. When an editable combo box is dropped
3338 * down using Alt+Down and the text in the entry field partially
3339 * matches an item in the list, Windows selects the item but doesn't
3340 * send WM_COMMAND with CBN_SELCHANGE. The fix is to detect that
3341 * the selection has changed and issue the notification.
3342 */
3343 int oldSelection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
3344 LRESULT result = super.wmSysKeyDown (hwnd, wParam, lParam);
3345 if (result != null) return result;
3346 if ((style & SWT.READ_ONLY) == 0) {
3347 if (wParam == OS.VK_DOWN) {
3348 long code = callWindowProc (hwnd, OS.WM_SYSKEYDOWN, wParam, lParam);
3349 int newSelection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
3350 if (oldSelection != newSelection) {
3351 sendEvent (SWT.Modify);
3352 if (isDisposed ()) return LRESULT.ZERO;
3353 sendSelectionEvent (SWT.Selection, null, true);
3354 if (isDisposed ()) return LRESULT.ZERO;
3355 }
3356 return new LRESULT (code);
3357 }
3358 }
3359 return result;
3360 }
3361
3362 }
3363