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