1 /*******************************************************************************
2 * Copyright (c) 2000, 2019 IBM Corporation and others.
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/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 * Lars Vogel <Lars.Vogel@vogella.com> - Bug 483540
14 *******************************************************************************/
15 package org.eclipse.swt.widgets;
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.*;
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.
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.
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.
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>
52 * Note: Only one of the styles DROP_DOWN and SIMPLE may be specified.
54 * IMPORTANT: This class is <em>not</em> intended to be subclassed.
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.
63 public class Combo extends Composite {
64 boolean noSelection, ignoreDefaultSelection, ignoreCharacter, ignoreModify, ignoreResize, lockText;
65 int scrollWidth, visibleCount;
67 String [] items = new String [0];
69 int clearSegmentsCount = 0;
70 boolean stateFlagsUsable;
72 static final char LTR_MARK = '\u200e';
73 static final char RTL_MARK = '\u200f';
74 static final int VISIBLE_COUNT = 5;
77 * the operating system limit for the number of characters
78 * that the text field in an instance of this class can hold
80 public static final int LIMIT;
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.
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).
97 static final int CBID_LIST = 1000;
98 static final int CBID_EDIT = 1001;
99 static /*final*/ long EditProc, ListProc;
101 static final long ComboProc;
102 static final TCHAR ComboClass = new TCHAR (0, "COMBOBOX", true);
104 WNDCLASS lpWndClass = new WNDCLASS ();
105 OS.GetClassInfo (0, ComboClass, lpWndClass);
106 ComboProc = lpWndClass.lpfnWndProc;
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;
114 * Constructs a new instance of this class given its parent
115 * and a style value describing its behavior and appearance.
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.
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
129 * @exception IllegalArgumentException <ul>
130 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
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>
140 * @see Widget#checkSubclass
141 * @see Widget#getStyle
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;
151 * Adds the argument to the end of the receiver's list.
153 * Note: If control characters like '\n', '\t' etc. are used
154 * in the string, then the behavior is platform dependent.
156 * @param string the new item
158 * @exception IllegalArgumentException <ul>
159 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
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>
166 * @see #add(String,int)
168 public void add (String string) {
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);
179 * Adds the argument to the receiver's list at the given
180 * zero-relative index.
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>.
186 * Also note, if control characters like '\n', '\t' etc. are used
187 * in the string, then the behavior is platform dependent.
190 * @param string the new item
191 * @param index the index for the item
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>
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>
204 public void add (String string, int index) {
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);
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);
216 if ((style & SWT.H_SCROLL) != 0) setScrollWidth (buffer.chars, true);
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>
225 * @param listener the listener which should be notified
227 * @exception IllegalArgumentException <ul>
228 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
235 * @see ModifyListener
236 * @see #removeModifyListener
238 public void addModifyListener (ModifyListener listener) {
240 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
241 TypedListener typedListener = new TypedListener (listener);
242 addListener (SWT.Modify, typedListener);
246 * Adds a segment listener.
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
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.
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.
263 * @param listener the listener which should be notified
265 * @exception IllegalArgumentException <ul>
266 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
274 * @see SegmentListener
275 * @see #removeSegmentListener
279 public void addSegmentListener (SegmentListener listener) {
281 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
282 addListener (SWT.Segments, new TypedListener (listener));
283 int selection = OS.CB_ERR;
285 selection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
287 clearSegments (true);
288 applyEditSegments ();
289 applyListSegments ();
290 if (selection != OS.CB_ERR) {
291 OS.SendMessage (handle, OS.CB_SETCURSEL, selection, 0);
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>
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.
305 * @param listener the listener which should be notified
307 * @exception IllegalArgumentException <ul>
308 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
315 * @see SelectionListener
316 * @see #removeSelectionListener
317 * @see SelectionEvent
319 public void addSelectionListener(SelectionListener listener) {
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);
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>
333 * @param listener the listener which should be notified
335 * @exception IllegalArgumentException <ul>
336 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
343 * @see VerifyListener
344 * @see #removeVerifyListener
348 public void addVerifyListener (VerifyListener listener) {
350 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
351 TypedListener typedListener = new TypedListener (listener);
352 addListener (SWT.Verify, typedListener);
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);
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);
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;
382 } else if (string != null) {
383 newChars [charCount] = string.charAt (charCount++ - segmentCount);
386 while (segmentCount < nSegments) {
387 segments [segmentCount] = charCount - segmentCount;
388 char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars [segmentCount] : defaultSeparator;
389 newChars [charCount++] = separator;
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;
398 * SetWindowText empties the undo buffer and disables undo menu item.
399 * Sending OS.EM_REPLACESEL message instead.
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) {
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.
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);
423 OS.SendMessage (hwndText, OS.EM_SETSEL, start [0], end [0]);
424 ignoreCharacter = oldIgnoreCharacter;
425 ignoreModify = oldIgnoreModify;
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 ();
439 selection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
441 while (index-- > 0) {
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);
450 string = items [index];
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;
456 if (selection != OS.CB_ERR) {
457 OS.SendMessage (handle, OS.CB_SETCURSEL, selection, 0);
462 long callWindowProc (long hwnd, int msg, long wParam, long lParam) {
463 if (handle == 0) return 0;
464 if (hwnd == handle) {
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;
476 return OS.CallWindowProc (ComboProc, hwnd, msg, wParam, lParam);
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);
492 return OS.CallWindowProc (EditProc, hwnd, msg, wParam, lParam);
494 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
495 if (hwnd == hwndList) {
496 return OS.CallWindowProc (ListProc, hwnd, msg, wParam, lParam);
498 return OS.DefWindowProc (hwnd, msg, wParam, lParam);
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);
511 return OS.CallNextHookEx (cbtHook, (int)nCode, wParam, lParam);
515 boolean checkHandle (long hwnd) {
516 return hwnd == handle || hwnd == OS.GetDlgItem (handle, CBID_EDIT) || hwnd == OS.GetDlgItem (handle, CBID_LIST);
520 protected void checkSubclass () {
521 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
524 static int checkStyle (int style) {
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.
534 * The Windows behavior is currently implemented on
537 style &= ~SWT.BORDER;
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
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;
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;
560 OS.SendMessage (hwndText, OS.EM_SETLIMITTEXT, Math.max (1, limit - nSegments), 0);
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]);
581 * SetWindowText empties the undo buffer and disables undo in the context
582 * menu. Sending OS.EM_REPLACESEL message instead.
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;
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.
598 * Note: To clear the selected items in the receiver's list,
599 * use <code>deselectAll()</code>.
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>
609 public void clearSelection () {
611 OS.SendMessage (handle, OS.CB_SETEDITSEL, 0, -1);
614 @Override Point computeSizeInPixels (int wHint, int hHint, boolean changed) {
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);
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);
646 if (newFont != 0) OS.SelectObject (hDC, oldFont);
647 OS.ReleaseDC (handle, hDC);
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;
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) {
663 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
665 long margins = OS.SendMessage (hwndText, OS.EM_GETMARGINS, 0, 0);
666 int marginWidth = OS.LOWORD (margins) + OS.HIWORD (margins);
667 width += marginWidth + 3;
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;
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;
682 height += textHeight + 10;
685 if ((style & SWT.SIMPLE) != 0 && (style & SWT.H_SCROLL) != 0) {
686 height += OS.GetSystemMetrics (OS.SM_CYHSCROLL);
688 return new Point (width, height);
692 * Copies the selected text.
694 * The current selection is copied to the clipboard.
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>
704 public void copy () {
706 OS.SendMessage (handle, OS.WM_COPY, 0, 0);
710 void createHandle () {
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.
721 if ((style & (SWT.READ_ONLY | SWT.SIMPLE)) != 0) {
722 super.createHandle ();
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);
732 cbtCallback.dispose ();
734 state &= ~(CANVAS | THEME_BACKGROUND);
736 stateFlagsUsable = stateFlagsTest();
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);
743 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
744 if (hwndList != 0 && ListProc == 0) {
745 ListProc = OS.GetWindowLongPtr (hwndList, OS.GWLP_WNDPROC);
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.
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);
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) {
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);
780 * Cuts the selected text.
782 * The current selection is first copied to the
783 * clipboard and then deleted from the widget.
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>
795 if ((style & SWT.READ_ONLY) != 0) return;
796 OS.SendMessage (handle, OS.WM_CUT, 0, 0);
800 int defaultBackground () {
801 return OS.GetSysColor (OS.COLOR_WINDOW);
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;
811 if (start < 0) start = 0;
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]) {
823 chars [i - segmentCount + nLeadSegments] = chars [i];
826 length = end - start - segmentCount + nLeadSegments;
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);
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);
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.
850 * @param index the index of the item to deselect
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>
857 public void deselect (int index) {
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--;
869 * Deselects all selected items in the receiver's list.
871 * Note: To clear the selection in the receiver's text field,
872 * use <code>clearSelection()</code>.
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>
880 * @see #clearSelection
882 public void deselectAll () {
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--;
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);
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;
911 return super.dragDetect (hwnd, x, y, filter, detect, consume);
915 * Returns a point describing the location of the caret relative
918 * @return a point, the location of the caret
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>
927 public Point getCaretLocation () {
929 return DPIUtil.autoScaleDown(getCaretLocationInPixels());
932 Point getCaretLocationInPixels () {
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).
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) {
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);
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.
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]);
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);
982 * Returns the character position of the caret.
984 * Indexing is zero based.
987 * @return the position of the caret
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>
996 public int getCaretPosition () {
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);
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.
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
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);
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];
1026 int endX = OS.GET_X_LPARAM (endPos);
1027 if (ptCurrentPos.x >= endX) caret = end [0];
1033 return untranslateOffset (caret);
1037 * Returns the item at the given, zero-relative index in the
1038 * receiver's list. Throws an exception if the index is out
1041 * @param index the index of the item to return
1042 * @return the item at the given index
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>
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>
1052 public String getItem (int index) {
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);
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);
1068 * Returns the number of items contained in the receiver's list.
1070 * @return the number of items
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>
1077 public int getItemCount () {
1079 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
1080 if (count == OS.CB_ERR) error (SWT.ERROR_CANNOT_GET_COUNT);
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.
1088 * @return the height of one item
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>
1095 public int getItemHeight () {
1097 return DPIUtil.autoScaleDown(getItemHeightInPixels());
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);
1107 * Returns a (possibly empty) array of <code>String</code>s which are
1108 * the items in the receiver's list.
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.
1115 * @return the items in the receiver's list
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>
1122 public String [] getItems () {
1125 int count = getItemCount ();
1126 result = new String [count];
1127 for (int i=0; i<count; i++) result [i] = getItem (i);
1132 * Returns <code>true</code> if the receiver's list is visible,
1133 * and <code>false</code> otherwise.
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.
1141 * @return the receiver's list's visibility state
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>
1150 public boolean getListVisible () {
1152 if ((style & SWT.DROP_DOWN) != 0) {
1153 return OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0;
1159 String getNameText () {
1164 * Marks the receiver's list as visible if the argument is <code>true</code>,
1165 * and marks it invisible otherwise.
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.
1172 * @param visible the new visibility state
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>
1181 public void setListVisible (boolean visible) {
1183 OS.SendMessage (handle, OS.CB_SHOWDROPDOWN, visible ? 1 : 0, 0);
1187 * Returns the orientation of the receiver.
1189 * @return the orientation style
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>
1199 public int getOrientation () {
1200 return super.getOrientation ();
1203 Event getSegments (String string) {
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);
1217 if ((state & HAS_AUTO_DIRECTION) != 0) {
1218 int direction = BidiUtil.resolveTextDirection(string);
1219 if (direction == SWT.NONE) {
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.
1225 direction = (style & SWT.RIGHT_TO_LEFT) != 0 ? SWT.RIGHT_TO_LEFT : SWT.LEFT_TO_RIGHT;
1227 int [] oldSegments = null;
1228 char [] oldSegmentsChars = null;
1229 if (event == null) {
1230 event = new Event ();
1232 oldSegments = event.segments;
1233 oldSegmentsChars = event.segmentsChars;
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);
1241 if (oldSegmentsChars != null) {
1242 System.arraycopy(oldSegmentsChars, 0, event.segmentsChars, 1, nSegments);
1244 event.segments [0] = 0;
1245 event.segmentsChars [0] = direction == SWT.RIGHT_TO_LEFT ? RLE : LRE;
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;
1268 newChars[charCount + segmentCount] = oldChars[charCount++];
1271 while (segmentCount < nSegments) {
1272 segments[segmentCount] = charCount;
1273 char separator = segmentsChars != null && segmentsChars.length > segmentCount ? segmentsChars[segmentCount] : defaultSeparator;
1274 newChars[charCount + segmentCount++] = separator;
1276 return new String(newChars, 0, newChars.length);
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.
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.
1291 * @return a point representing the selection start and end
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>
1298 public Point getSelection () {
1300 if ((style & SWT.DROP_DOWN) != 0 && (style & SWT.READ_ONLY) != 0) {
1301 return new Point (0, OS.GetWindowTextLength (handle));
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]));
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.
1312 * @return the index of the selected item
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>
1319 public int getSelectionIndex () {
1321 if (noSelection) return -1;
1322 return (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
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
1330 * @return the receiver's text
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>
1337 public String getText () {
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 ();
1348 return buffer.toString (0, length);
1352 * Returns the height of the receivers's text field.
1354 * @return the text height
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>
1361 public int getTextHeight () {
1363 return DPIUtil.autoScaleDown(getTextHeightInPixels());
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;
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;
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>.
1383 * @return the text limit
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>
1392 public int getTextLimit () {
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);
1402 * Gets the number of items that are visible in the drop
1403 * down portion of the receiver's list.
1405 * Note: This operation is a hint and is not supported on
1406 * platforms that do not have this concept.
1409 * @return the number of items that are visible
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>
1418 public int getVisibleItemCount () {
1420 return visibleCount;
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;
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.
1441 * @param string the search item
1442 * @return the index of the item
1444 * @exception IllegalArgumentException <ul>
1445 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
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>
1452 public int indexOf (String string) {
1453 return indexOf (string, 0);
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,
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
1467 * @exception IllegalArgumentException <ul>
1468 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
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>
1475 public int indexOf (String string, int start) {
1477 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
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.
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;
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);
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)));
1506 * Pastes text from clipboard.
1508 * The selected text is deleted from the widget
1509 * and new text inserted from the clipboard.
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>
1519 public void paste () {
1521 if ((style & SWT.READ_ONLY) != 0) return;
1522 OS.SendMessage (handle, OS.WM_PASTE, 0, 0);
1525 void stateFlagsAdd(int flags) {
1526 final long tagCBoxPtr = OS.GetWindowLongPtr(handle, 0);
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.
1532 if (tagCBoxPtr == 0) return;
1533 final long stateFlagsPtr = tagCBoxPtr + stateFlagsOffset;
1535 int stateFlags[] = new int[1];
1536 OS.MoveMemory(stateFlags, stateFlagsPtr, 4);
1537 stateFlags[0] |= flags;
1538 OS.MoveMemory(stateFlagsPtr, stateFlags, 4);
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.
1546 boolean stateFlagsTest() {
1547 final long tagCBoxPtr = OS.GetWindowLongPtr(handle, 0);
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.
1553 if (tagCBoxPtr == 0) return false;
1554 final long stateFlagsPtr = tagCBoxPtr + stateFlagsOffset;
1556 int stateFlags[] = new int[1];
1557 OS.MoveMemory(stateFlags, stateFlagsPtr, 4);
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
1565 return (stateFlags[0] == 0x02006002);
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);
1578 * Removes the item from the receiver's list at the given
1579 * zero-relative index.
1581 * @param index the index for the item
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>
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>
1591 public void remove (int index) {
1593 remove (index, true);
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);
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);
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);
1620 else if (code == 0) {
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
1626 OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0);
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;
1636 * Removes the items from the receiver's list which are
1637 * between the given zero-relative start and end
1638 * indices (inclusive).
1640 * @param start the start of the range
1641 * @param end the end of the range
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>
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>
1651 public void remove (int start, int end) {
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);
1658 int textLength = OS.GetWindowTextLength (handle);
1660 long hDC = 0, oldFont = 0, newFont = 0;
1662 if ((style & SWT.H_SCROLL) != 0) {
1664 hDC = OS.GetDC (handle);
1665 newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
1666 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
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;
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);
1682 else if (result == 0) {
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
1688 OS.SendMessage (handle, OS.CB_RESETCONTENT, 0, 0);
1690 if ((style & SWT.H_SCROLL) != 0) {
1691 OS.DrawText (hDC, buffer, -1, rect, flags);
1692 newWidth = Math.max (newWidth, rect.right - rect.left);
1695 if ((style & SWT.H_SCROLL) != 0) {
1696 if (newFont != 0) OS.SelectObject (hDC, oldFont);
1697 OS.ReleaseDC (handle, hDC);
1698 setScrollWidth (newWidth, false);
1700 if (textLength != OS.GetWindowTextLength (handle)) {
1701 sendEvent (SWT.Modify);
1702 if (isDisposed ()) return;
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.
1711 * @param string the item to remove
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>
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>
1722 public void remove (String string) {
1724 if (string == null) error (SWT.ERROR_NULL_ARGUMENT);
1725 int index = indexOf (string, 0);
1726 if (index == -1) error (SWT.ERROR_INVALID_ARGUMENT);
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>
1738 public void removeAll () {
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);
1747 * Removes the listener from the collection of listeners who will
1748 * be notified when the receiver's text is modified.
1750 * @param listener the listener which should no longer be notified
1752 * @exception IllegalArgumentException <ul>
1753 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
1760 * @see ModifyListener
1761 * @see #addModifyListener
1763 public void removeModifyListener (ModifyListener listener) {
1765 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
1766 if (eventTable == null) return;
1767 eventTable.unhook (SWT.Modify, listener);
1771 * Removes the listener from the collection of listeners who will
1772 * be notified when the receiver's text is modified.
1774 * @param listener the listener which should no longer be notified
1776 * @exception IllegalArgumentException <ul>
1777 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
1785 * @see SegmentListener
1786 * @see #addSegmentListener
1790 public void removeSegmentListener (SegmentListener listener) {
1792 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
1793 eventTable.unhook (SWT.Segments, listener);
1794 int selection = OS.CB_ERR;
1796 selection = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
1798 clearSegments (true);
1799 applyEditSegments ();
1800 applyListSegments ();
1801 if (selection != OS.CB_ERR) {
1802 OS.SendMessage (handle, OS.CB_SETCURSEL, selection, 0);
1806 * Removes the listener from the collection of listeners who will
1807 * be notified when the user changes the receiver's selection.
1809 * @param listener the listener which should no longer be notified
1811 * @exception IllegalArgumentException <ul>
1812 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
1819 * @see SelectionListener
1820 * @see #addSelectionListener
1822 public void removeSelectionListener (SelectionListener listener) {
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);
1831 * Removes the listener from the collection of listeners who will
1832 * be notified when the control is verified.
1834 * @param listener the listener which should no longer be notified
1836 * @exception IllegalArgumentException <ul>
1837 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
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>
1844 * @see VerifyListener
1845 * @see #addVerifyListener
1849 public void removeVerifyListener (VerifyListener listener) {
1851 if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
1852 if (eventTable == null) return;
1853 eventTable.unhook (SWT.Verify, listener);
1857 boolean sendKeyEvent (int type, int msg, long wParam, long lParam, Event event) {
1858 if (!super.sendKeyEvent (type, msg, wParam, lParam, event)) {
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) {
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;
1872 * Disable all magic keys that could modify the text
1873 * and don't send events when Alt, Shift or Ctrl is
1878 if (key != 0x08 && key != 0x7F && key != '\r' && key != '\t' && key != '\n') break;
1881 if ((stateMask & (SWT.ALT | SWT.SHIFT | SWT.CONTROL)) != 0) return false;
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
1891 if (OS.GetKeyState (OS.VK_LBUTTON) < 0) {
1892 if (OS.GetDlgItem (handle, CBID_EDIT) == OS.GetCapture()) return true;
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);
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);
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);
1917 case '\r': /* Return */
1919 default: /* Tab and other characters */
1920 if (key != '\t' && key < 0x20) return true;
1921 oldText = new String (new char [] {key});
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);
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.
1938 * @param index the index of the item to select
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>
1945 public void select (int index) {
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);
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;
1963 firstKey = OS.VK_DOWN;
1964 secondKey = OS.VK_UP;
1966 OS.SendMessage (handle, OS.WM_KEYDOWN, firstKey, 0);
1967 OS.SendMessage (handle, OS.WM_KEYDOWN, secondKey, 0);
1969 sendEvent (SWT.Modify);
1970 // widget could be disposed at this point
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);
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);
1994 void setBoundsInPixels (int x, int y, int width, int height, int flags) {
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.
2012 if ((style & SWT.DROP_DOWN) != 0) {
2013 int visibleCount = getItemCount() == 0 ? VISIBLE_COUNT : this.visibleCount;
2014 height = getTextHeightInPixels () + (getItemHeightInPixels () * visibleCount) + 2;
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.
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.
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;
2036 OS.SetWindowPos (handle, 0, x, y, width, height, flags);
2038 super.setBoundsInPixels (x, y, width, height, flags);
2043 public void setFont (Font font) {
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
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 ();
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);
2072 * Sets the text of the item in the receiver's list at the given
2073 * zero-relative index to the string argument.
2075 * @param index the index for the item
2076 * @param string the new text for the item
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>
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>
2087 public void setItem (int index, String string) {
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);
2098 * Sets the receiver's list to be the given array of items.
2100 * @param items the array of items
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>
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>
2111 public void setItems (String... items) {
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);
2118 long hDC = 0, oldFont = 0, newFont = 0;
2120 if ((style & SWT.H_SCROLL) != 0) {
2122 hDC = OS.GetDC (handle);
2123 newFont = OS.SendMessage (handle, OS.WM_GETFONT, 0, 0);
2124 if (newFont != 0) oldFont = OS.SelectObject (hDC, newFont);
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);
2141 if ((style & SWT.H_SCROLL) != 0) {
2142 if (newFont != 0) OS.SelectObject (hDC, oldFont);
2143 OS.ReleaseDC (handle, hDC);
2144 setScrollWidth (newWidth + 3);
2146 sendEvent (SWT.Modify);
2147 // widget could be disposed at this point
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>.
2155 * @param orientation new orientation style
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>
2165 public void setOrientation (int orientation) {
2166 super.setOrientation (orientation);
2169 void setScrollWidth () {
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);
2189 if (newFont != 0) OS.SelectObject (hDC, oldFont);
2190 OS.ReleaseDC (handle, hDC);
2191 setScrollWidth (newWidth + 3);
2194 void setScrollWidth (int scrollWidth) {
2195 this.scrollWidth = scrollWidth;
2196 if ((style & SWT.SIMPLE) != 0) {
2197 OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, scrollWidth, 0);
2200 boolean scroll = false;
2201 int count = (int)OS.SendMessage (handle, OS.CB_GETCOUNT, 0, 0);
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;
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.
2219 boolean oldLockText = lockText;
2220 if ((style & SWT.READ_ONLY) == 0) lockText = true;
2222 OS.SendMessage (handle, OS.CB_SETDROPPEDWIDTH, 0, 0);
2223 OS.SendMessage (handle, OS.CB_SETHORIZONTALEXTENT, scrollWidth, 0);
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);
2229 if ((style & SWT.READ_ONLY) == 0) lockText = oldLockText;
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);
2245 void setScrollWidth (int newWidth, boolean grow) {
2247 if (newWidth <= scrollWidth) return;
2248 setScrollWidth (newWidth + 3);
2250 if (newWidth < scrollWidth) return;
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
2261 * @param selection a point representing the new selection start and end
2263 * @exception IllegalArgumentException <ul>
2264 * <li>ERROR_NULL_ARGUMENT - if the point is null</li>
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>
2271 public void setSelection (Point selection) {
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);
2280 * Sets the contents of the receiver's text field to the
2283 * This call is ignored when the receiver is read only and
2284 * the given string is not in the receiver's list.
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.
2293 * Also note, if control characters like '\n', '\t' etc. are used
2294 * in the string, then the behavior is platform dependent.
2297 * @param string the new text
2299 * @exception IllegalArgumentException <ul>
2300 * <li>ERROR_NULL_ARGUMENT - if the string is null</li>
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>
2307 public void setText (String string) {
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);
2315 clearSegments (false);
2317 long hwndText = OS.GetDlgItem (handle, CBID_EDIT);
2318 if (hwndText != 0) {
2319 limit = (int)OS.SendMessage (hwndText, OS.EM_GETLIMITTEXT, 0, 0) & 0x7FFFFFFF;
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
2331 * Sets the maximum number of characters that the receiver's
2332 * text field is capable of holding to be the argument.
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>.
2338 * @param limit new text limit
2340 * @exception IllegalArgumentException <ul>
2341 * <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
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>
2350 public void setTextLimit (int limit) {
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);
2356 OS.SendMessage (handle, OS.CB_LIMITTEXT, limit, 0);
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);
2370 * Sets the number of items that are visible in the drop
2371 * down portion of the receiver's list.
2373 * Note: This operation is a hint and is not supported on
2374 * platforms that do not have this concept.
2377 * @param count the new number of items to be visible
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>
2386 public void setVisibleItemCount (int count) {
2388 if (count < 0) return;
2389 visibleCount = count;
2390 updateDropDownHeight ();
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);
2401 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
2402 if (hwndList != 0) {
2403 OS.SetWindowLongPtr (hwndList, OS.GWLP_WNDPROC, newProc);
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++) {
2416 boolean translateTraversal (MSG msg) {
2418 * When the combo box is dropped down, allow return
2419 * to select an item in the list and escape to close
2422 switch ((int)(msg.wParam)) {
2425 if ((style & SWT.DROP_DOWN) != 0) {
2426 if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) {
2431 return super.translateTraversal (msg);
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);
2442 return super.traverseEscape ();
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);
2453 return super.traverseReturn ();
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);
2463 long hwndList = OS.GetDlgItem (handle, CBID_LIST);
2464 if (hwndList != 0 && ListProc != 0) {
2465 OS.SetWindowLongPtr (hwndList, OS.GWLP_WNDPROC, ListProc);
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++) {
2477 void updateDropDownHeight () {
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.
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)) {
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);
2499 boolean updateTextDirection(int textDirection) {
2500 if (super.updateTextDirection(textDirection)) {
2501 clearSegments (true);
2502 applyEditSegments ();
2503 applyListSegments ();
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;
2515 bits &= ~OS.WS_EX_LAYOUTRTL;
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;
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;
2533 bits1 &= ~(OS.WS_EX_RIGHT | OS.WS_EX_RTLREADING);
2534 bits2 &= ~OS.ES_RIGHT;
2536 OS.SetWindowLong (hwndText, OS.GWL_EXSTYLE, bits1);
2537 OS.SetWindowLong (hwndText, OS.GWL_STYLE, bits2);
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.
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);
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;
2562 bits1 &= ~OS.WS_EX_LAYOUTRTL;
2564 OS.SetWindowLong (hwndList, OS.GWL_EXSTYLE, bits1);
2568 String verifyText (String string, int start, int end, Event keyEvent) {
2569 Event event = new Event ();
2570 event.text = string;
2571 event.start = start;
2573 if (keyEvent != null) {
2574 event.character = keyEvent.character;
2575 event.keyCode = keyEvent.keyCode;
2576 event.stateMask = keyEvent.stateMask;
2578 event.start = untranslateOffset (event.start);
2579 event.end = untranslateOffset (event.end);
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
2586 sendEvent (SWT.Verify, event);
2587 if (!event.doit || isDisposed ()) return null;
2592 int widgetExtStyle () {
2593 return super.widgetExtStyle () & ~OS.WS_EX_NOINHERITLAYOUT;
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;
2605 TCHAR windowClass () {
2610 long windowProc () {
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;
2624 /* Keyboard messages */
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);
2629 case OS.WM_IME_CHAR: result = wmIMEChar (hwnd, wParam, lParam); break;
2631 processSegments = wParam == OS.VK_DELETE && (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0));
2632 result = wmKeyDown (hwnd, wParam, lParam);
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;
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;
2658 /* Paint messages */
2659 case OS.WM_PAINT: result = wmPaint (hwnd, wParam, lParam); break;
2662 case OS.WM_CONTEXTMENU: result = wmContextMenu (hwnd, wParam, lParam); break;
2664 /* Clipboard messages */
2666 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) return 0;
2670 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) return 0;
2675 processSegments = hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0);
2677 if (hwnd == hwndText) {
2678 result = wmClipboard (hwnd, msg, wParam, lParam);
2682 if (result != null) return result.value;
2684 if (processSegments) {
2685 if (getDrawing () && OS.IsWindowVisible (hwndText)) {
2687 OS.DefWindowProc (hwndText, OS.WM_SETREDRAW, 0, 0);
2689 clearSegments (true);
2690 long code = callWindowProc (hwnd, msg, wParam, lParam);
2691 applyEditSegments ();
2693 OS.DefWindowProc (hwndText, OS.WM_SETREDRAW, 1, 0);
2694 OS.InvalidateRect (hwndText, null, true);
2698 return callWindowProc (hwnd, msg, wParam, lParam);
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;
2711 if (0 <= wParam && wParam < getItemCount ()) {
2712 newText = getItem ((int)wParam);
2715 if (newText != null && !newText.equals (oldText)) {
2716 int length = OS.GetWindowTextLength (handle);
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);
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);
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);
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);
2775 if (code != OS.CB_ERR && code != OS.CB_ERRSPACE) return code;
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);
2792 index = (int)OS.SendMessage (handle, OS.CB_GETCURSEL, 0, 0);
2793 if (index == wParam) {
2794 clearSegments (false);
2795 applyEditSegments ();
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 ();
2812 return super.windowProc (hwnd, msg, wParam, lParam);
2816 LRESULT wmColorChild (long wParam, long lParam) {
2817 LRESULT result = super.wmColorChild(wParam, lParam);
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.
2836 final boolean isReadonly = ((style & SWT.READ_ONLY) != 0);
2837 final boolean isCustomColors = (result != null);
2838 if (isReadonly && isCustomColors && stateFlagsUsable) {
2839 stateFlagsAdd(stateFlagsFirstPaint);
2846 LRESULT WM_CTLCOLOR (long wParam, long lParam) {
2847 return wmColorChild (wParam, lParam);
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);
2857 LRESULT WM_KILLFOCUS (long wParam, long lParam) {
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
2864 * NOTE: In version 6 of COMCTL32.DLL, the bug is fixed.
2866 if ((style & SWT.READ_ONLY) != 0) {
2867 return super.WM_KILLFOCUS (wParam, lParam);
2871 * Return NULL - Focus notification is
2872 * done in WM_COMMAND by CBN_KILLFOCUS.
2878 LRESULT WM_LBUTTONDOWN (long wParam, long lParam) {
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.
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;
2902 LRESULT WM_SETFOCUS (long wParam, long lParam) {
2904 * Return NULL - Focus notification is
2905 * done by WM_COMMAND with CBN_SETFOCUS.
2911 LRESULT WM_SIZE (long wParam, long lParam) {
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
2921 if (ignoreResize) return null;
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.
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);
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
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;
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.
2955 if ((style & SWT.H_SCROLL) != 0) setScrollWidth (scrollWidth);
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.
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);
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);
2991 LRESULT WM_WINDOWPOSCHANGING (long wParam, long lParam) {
2992 LRESULT result = super.WM_WINDOWPOSCHANGING (wParam, lParam);
2993 if (result != null) return result;
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.
3005 if (!getDrawing ()) return result;
3006 if (!OS.IsWindowVisible (handle)) return result;
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);
3031 hwndChild = OS.GetWindow (hwndChild, OS.GW_HWNDNEXT);
3033 int flags = OS.RDW_ERASE | OS.RDW_FRAME | OS.RDW_INVALIDATE;
3034 OS.RedrawWindow (hwndParent, null, rgn1, flags);
3035 OS.DeleteObject (rgn1);
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) {
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
3055 * NOTE: This only happens when the drop down list
3058 case SWT.TAB: return LRESULT.ZERO;
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);
3069 return LRESULT.ZERO;
3073 if ((style & SWT.DROP_DOWN) != 0) {
3074 if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) == 0) {
3075 return LRESULT.ZERO;
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.
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;
3093 String actText = getText ().substring (0, x);
3094 java.util.regex.Matcher m = CTRL_BS_PATTERN.matcher (actText);
3098 OS.SendMessage (hwndText, OS.EM_SETSEL, x, y);
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.
3106 OS.SendMessage (hwndText, OS.EM_REPLACESEL, 1, 0);
3108 return LRESULT.ZERO;
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;
3123 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
3124 if (untranslateOffset (start [0]) != untranslateOffset (end [0])) {
3130 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
3131 newText = getClipboardText ();
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]);
3148 OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
3149 OS.SendMessage (hwndText, OS.EM_GETSEL, start, end);
3150 ignoreModify = false;
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);
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)) {
3169 OS.CallWindowProc (EditProc, hwndText, msg, wParam, lParam);
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);
3181 OS.SendMessage (hwndText, OS.EM_REPLACESEL, 0, buffer);
3182 return LRESULT.ZERO;
3190 LRESULT wmCommandChild (long wParam, long lParam) {
3191 int code = OS.HIWORD (wParam);
3193 case OS.CBN_EDITCHANGE:
3194 if (ignoreModify) break;
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.
3205 sendEvent (SWT.Modify);
3206 if (isDisposed ()) return LRESULT.ZERO;
3207 noSelection = false;
3209 case OS.CBN_SELCHANGE:
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.
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);
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
3230 sendEvent (SWT.Modify);
3231 if (isDisposed ()) return LRESULT.ZERO;
3232 sendSelectionEvent (SWT.Selection);
3234 case OS.CBN_SETFOCUS:
3235 sendFocusEvent (SWT.FocusIn);
3236 if (isDisposed ()) return LRESULT.ZERO;
3238 case OS.CBN_DROPDOWN:
3240 updateDropDownHeight ();
3242 case OS.CBN_KILLFOCUS:
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
3249 * NOTE: In version 6 of COMCTL32.DLL, the bug is fixed.
3251 if ((style & SWT.READ_ONLY) != 0) break;
3252 sendFocusEvent (SWT.FocusOut);
3253 if (isDisposed ()) return LRESULT.ZERO;
3255 case OS.EN_ALIGN_LTR_EC:
3256 case OS.EN_ALIGN_RTL_EC:
3257 Event event = new Event ();
3259 sendEvent (SWT.OrientationChange, event);
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;
3268 bits1 &= ~(OS.WS_EX_RTLREADING | OS.WS_EX_RIGHT);
3269 bits2 &= ~OS.ES_RIGHT;
3271 OS.SetWindowLong (hwnd, OS.GWL_EXSTYLE, bits1);
3272 OS.SetWindowLong (hwnd, OS.GWL_STYLE, bits2);
3274 if (hooks (SWT.Segments) || filters (SWT.Segments) || ((state & HAS_AUTO_DIRECTION) != 0)) {
3275 clearSegments(true);
3277 * Explicit LTR or RTL direction was set, so auto direction
3278 * should be deactivated.
3280 state &= ~HAS_AUTO_DIRECTION;
3281 applyEditSegments();
3285 return super.wmCommandChild (wParam, lParam);
3289 LRESULT wmIMEChar (long hwnd, long wParam, long lParam) {
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;
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.
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);
3315 ignoreCharacter = false;
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);
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) {
3334 if (segments != null) {
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);
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;
3348 start [0] = newStart [0];
3349 end [0] = newEnd [0];
3351 result = code == 0 ? LRESULT.ZERO : new LRESULT (code);
3355 if ((style & SWT.DROP_DOWN) != 0) {
3356 if (OS.SendMessage (handle, OS.CB_GETDROPPEDSTATE, 0, 0) != 0) {
3357 ignoreDefaultSelection = true;
3366 LRESULT wmSysKeyDown (long hwnd, long wParam, long lParam) {
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.
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;
3387 return new LRESULT (code);