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