1 /*******************************************************************************
2  * Copyright (c) 2000, 2008 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  * Port to the D programming language:
11  *     Frank Benoit <benoit@tionex.de>
12  *******************************************************************************/
13 module org.eclipse.swt.custom.CCombo;
14 
15 
16 
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.SWTException;
19 import org.eclipse.swt.accessibility.ACC;
20 import org.eclipse.swt.accessibility.AccessibleAdapter;
21 import org.eclipse.swt.accessibility.AccessibleControlAdapter;
22 import org.eclipse.swt.accessibility.AccessibleControlEvent;
23 import org.eclipse.swt.accessibility.AccessibleEvent;
24 import org.eclipse.swt.accessibility.AccessibleTextAdapter;
25 import org.eclipse.swt.accessibility.AccessibleTextEvent;
26 import org.eclipse.swt.events.ModifyListener;
27 import org.eclipse.swt.events.SelectionEvent;
28 import org.eclipse.swt.events.SelectionListener;
29 import org.eclipse.swt.events.VerifyListener;
30 import org.eclipse.swt.graphics.Color;
31 import org.eclipse.swt.graphics.Font;
32 import org.eclipse.swt.graphics.GC;
33 import org.eclipse.swt.graphics.Point;
34 import org.eclipse.swt.graphics.Rectangle;
35 import org.eclipse.swt.widgets.Button;
36 import org.eclipse.swt.widgets.Composite;
37 import org.eclipse.swt.widgets.Control;
38 import org.eclipse.swt.widgets.Display;
39 import org.eclipse.swt.widgets.Event;
40 import org.eclipse.swt.widgets.Label;
41 import org.eclipse.swt.widgets.Layout;
42 import org.eclipse.swt.widgets.List;
43 import org.eclipse.swt.widgets.Listener;
44 import org.eclipse.swt.widgets.Menu;
45 import org.eclipse.swt.widgets.Shell;
46 import org.eclipse.swt.widgets.Text;
47 import org.eclipse.swt.widgets.TypedListener;
48 import org.eclipse.swt.widgets.Widget;
49 
50 import java.lang.all;
51 import java.nonstandard.UnsafeUtf;
52 
53 /**
54  * The CCombo class represents a selectable user interface object
55  * that combines a text field and a list and issues notification
56  * when an item is selected from the list.
57  * <p>
58  * CCombo was written to work around certain limitations in the native
59  * combo box. Specifically, on win32, the height of a CCombo can be set;
60  * attempts to set the height of a Combo are ignored. CCombo can be used
61  * anywhere that having the increased flexibility is more important than
62  * getting native L&F, but the decision should not be taken lightly.
63  * There is no is no strict requirement that CCombo look or behave
64  * the same as the native combo box.
65  * </p>
66  * <p>
67  * Note that although this class is a subclass of <code>Composite</code>,
68  * it does not make sense to add children to it, or set a layout on it.
69  * </p>
70  * <dl>
71  * <dt><b>Styles:</b>
72  * <dd>BORDER, READ_ONLY, FLAT</dd>
73  * <dt><b>Events:</b>
74  * <dd>DefaultSelection, Modify, Selection, Verify</dd>
75  * </dl>
76  *
77  * @see <a href="http://www.eclipse.org/swt/snippets/#ccombo">CCombo snippets</a>
78  * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: CustomControlExample</a>
79  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a>
80  */
81 public final class CCombo : Composite {
82 
83     alias Composite.computeSize computeSize;
84 
85     Text text;
86     List list;
87     int visibleItemCount = 5;
88     Shell popup;
89     Button arrow;
90     bool hasFocus;
91     Listener listener, filter;
92     Color foreground, background;
93     Font font;
94 
95 /**
96  * Constructs a new instance of this class given its parent
97  * and a style value describing its behavior and appearance.
98  * <p>
99  * The style value is either one of the style constants defined in
100  * class <code>SWT</code> which is applicable to instances of this
101  * class, or must be built by <em>bitwise OR</em>'ing together
102  * (that is, using the <code>int</code> "|" operator) two or more
103  * of those <code>SWT</code> style constants. The class description
104  * lists the style constants that are applicable to the class.
105  * Style bits are also inherited from superclasses.
106  * </p>
107  *
108  * @param parent a widget which will be the parent of the new instance (cannot be null)
109  * @param style the style of widget to construct
110  *
111  * @exception IllegalArgumentException <ul>
112  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
113  * </ul>
114  * @exception SWTException <ul>
115  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
116  * </ul>
117  *
118  * @see SWT#BORDER
119  * @see SWT#READ_ONLY
120  * @see SWT#FLAT
121  * @see Widget#getStyle()
122  */
123 public this (Composite parent, int style) {
124     super (parent, style = checkStyle (style));
125 
126     int textStyle = SWT.SINGLE;
127     if ((style & SWT.READ_ONLY) !is 0) textStyle |= SWT.READ_ONLY;
128     if ((style & SWT.FLAT) !is 0) textStyle |= SWT.FLAT;
129     text = new Text (this, textStyle);
130     int arrowStyle = SWT.ARROW | SWT.DOWN;
131     if ((style & SWT.FLAT) !is 0) arrowStyle |= SWT.FLAT;
132     arrow = new Button (this, arrowStyle);
133 
134     listener = new class() Listener {
135         public void handleEvent (Event event) {
136             if (popup is event.widget) {
137                 popupEvent (event);
138                 return;
139             }
140             if (text is event.widget) {
141                 textEvent (event);
142                 return;
143             }
144             if (list is event.widget) {
145                 listEvent (event);
146                 return;
147             }
148             if (arrow is event.widget) {
149                 arrowEvent (event);
150                 return;
151             }
152             if (this.outer is event.widget) {
153                 comboEvent (event);
154                 return;
155             }
156             if (getShell () is event.widget) {
157                 getDisplay().asyncExec(new class() Runnable {
158                     public void run() {
159                         if (isDisposed()) return;
160                         handleFocus (SWT.FocusOut);
161                     }
162                 });
163             }
164         }
165     };
166     filter = new class() Listener {
167         public void handleEvent(Event event) {
168             Shell shell = (cast(Control)event.widget).getShell ();
169             if (shell is this.outer.getShell ()) {
170                 handleFocus (SWT.FocusOut);
171             }
172         }
173     };
174 
175     int [] comboEvents = [SWT.Dispose, SWT.FocusIn, SWT.Move, SWT.Resize];
176     for (int i=0; i<comboEvents.length; i++) this.addListener (comboEvents [i], listener);
177 
178     int [] textEvents = [SWT.DefaultSelection, SWT.KeyDown, SWT.KeyUp, SWT.MenuDetect, SWT.Modify, SWT.MouseDown, SWT.MouseUp, SWT.MouseDoubleClick, SWT.MouseWheel, SWT.Traverse, SWT.FocusIn, SWT.Verify];
179     for (int i=0; i<textEvents.length; i++) text.addListener (textEvents [i], listener);
180 
181     int [] arrowEvents = [SWT.MouseDown, SWT.MouseUp, SWT.Selection, SWT.FocusIn];
182     for (int i=0; i<arrowEvents.length; i++) arrow.addListener (arrowEvents [i], listener);
183 
184     createPopup(null, -1);
185     initAccessible();
186 }
187 static int checkStyle (int style) {
188     int mask = SWT.BORDER | SWT.READ_ONLY | SWT.FLAT | SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
189     return SWT.NO_FOCUS | (style & mask);
190 }
191 /**
192  * Adds the argument to the end of the receiver's list.
193  *
194  * @param string the new item
195  *
196  * @exception SWTException <ul>
197  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
198  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
199  * </ul>
200  *
201  * @see #add(String,int)
202  */
203 public void add (String string) {
204     checkWidget();
205     // SWT extension: allow null for zero length string
206     //if (string is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
207     list.add (string);
208 }
209 /**
210  * Adds the argument to the receiver's list at the given
211  * zero-relative index.
212  * <p>
213  * Note: To add an item at the end of the list, use the
214  * result of calling <code>getItemCount()</code> as the
215  * index or use <code>add(String)</code>.
216  * </p>
217  *
218  * @param string the new item
219  * @param index the index for the item
220  *
221  * @exception IllegalArgumentException <ul>
222  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li>
223  * </ul>
224  * @exception SWTException <ul>
225  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
226  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
227  * </ul>
228  *
229  * @see #add(String)
230  */
231 public void add (String string, int index) {
232     checkWidget();
233     // SWT extension: allow null for zero length string
234     //if (string is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
235     list.add (string, index);
236 }
237 /**
238  * Adds the listener to the collection of listeners who will
239  * be notified when the receiver's text is modified, by sending
240  * it one of the messages defined in the <code>ModifyListener</code>
241  * interface.
242  *
243  * @param listener the listener which should be notified
244  *
245  * @exception IllegalArgumentException <ul>
246  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
247  * </ul>
248  * @exception SWTException <ul>
249  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
250  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
251  * </ul>
252  *
253  * @see ModifyListener
254  * @see #removeModifyListener
255  */
256 public void addModifyListener (ModifyListener listener) {
257     checkWidget();
258     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
259     TypedListener typedListener = new TypedListener (listener);
260     addListener (SWT.Modify, typedListener);
261 }
262 /**
263  * Adds the listener to the collection of listeners who will
264  * be notified when the user changes the receiver's selection, by sending
265  * it one of the messages defined in the <code>SelectionListener</code>
266  * interface.
267  * <p>
268  * <code>widgetSelected</code> is called when the combo's list selection changes.
269  * <code>widgetDefaultSelected</code> is typically called when ENTER is pressed the combo's text area.
270  * </p>
271  *
272  * @param listener the listener which should be notified when the user changes the receiver's selection
273  *
274  * @exception IllegalArgumentException <ul>
275  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
276  * </ul>
277  * @exception SWTException <ul>
278  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
279  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
280  * </ul>
281  *
282  * @see SelectionListener
283  * @see #removeSelectionListener
284  * @see SelectionEvent
285  */
286 public void addSelectionListener(SelectionListener listener) {
287     checkWidget();
288     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
289     TypedListener typedListener = new TypedListener (listener);
290     addListener (SWT.Selection,typedListener);
291     addListener (SWT.DefaultSelection,typedListener);
292 }
293 /**
294  * Adds the listener to the collection of listeners who will
295  * be notified when the receiver's text is verified, by sending
296  * it one of the messages defined in the <code>VerifyListener</code>
297  * interface.
298  *
299  * @param listener the listener which should be notified
300  *
301  * @exception IllegalArgumentException <ul>
302  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
303  * </ul>
304  * @exception SWTException <ul>
305  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
306  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
307  * </ul>
308  *
309  * @see VerifyListener
310  * @see #removeVerifyListener
311  *
312  * @since 3.3
313  */
314 public void addVerifyListener (VerifyListener listener) {
315     checkWidget();
316     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
317     TypedListener typedListener = new TypedListener (listener);
318     addListener (SWT.Verify,typedListener);
319 }
320 void arrowEvent (Event event) {
321     switch (event.type) {
322         case SWT.FocusIn: {
323             handleFocus (SWT.FocusIn);
324             break;
325         }
326         case SWT.MouseDown: {
327             Event mouseEvent = new Event ();
328             mouseEvent.button = event.button;
329             mouseEvent.count = event.count;
330             mouseEvent.stateMask = event.stateMask;
331             mouseEvent.time = event.time;
332             mouseEvent.x = event.x; mouseEvent.y = event.y;
333             notifyListeners (SWT.MouseDown, mouseEvent);
334             event.doit = mouseEvent.doit;
335             break;
336         }
337         case SWT.MouseUp: {
338             Event mouseEvent = new Event ();
339             mouseEvent.button = event.button;
340             mouseEvent.count = event.count;
341             mouseEvent.stateMask = event.stateMask;
342             mouseEvent.time = event.time;
343             mouseEvent.x = event.x; mouseEvent.y = event.y;
344             notifyListeners (SWT.MouseUp, mouseEvent);
345             event.doit = mouseEvent.doit;
346             break;
347         }
348         case SWT.Selection: {
349             text.setFocus();
350             dropDown (!isDropped ());
351             break;
352         }
353         default:
354     }
355 }
356 /**
357  * Sets the selection in the receiver's text field to an empty
358  * selection starting just before the first character. If the
359  * text field is editable, this has the effect of placing the
360  * i-beam at the start of the text.
361  * <p>
362  * Note: To clear the selected items in the receiver's list,
363  * use <code>deselectAll()</code>.
364  * </p>
365  *
366  * @exception SWTException <ul>
367  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
368  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
369  * </ul>
370  *
371  * @see #deselectAll
372  */
373 public void clearSelection () {
374     checkWidget ();
375     text.clearSelection ();
376     list.deselectAll ();
377 }
378 void comboEvent (Event event) {
379     switch (event.type) {
380         case SWT.Dispose:
381             if (popup !is null && !popup.isDisposed ()) {
382                 list.removeListener (SWT.Dispose, listener);
383                 popup.dispose ();
384             }
385             Shell shell = getShell ();
386             shell.removeListener (SWT.Deactivate, listener);
387             Display display = getDisplay ();
388             display.removeFilter (SWT.FocusIn, filter);
389             popup = null;
390             text = null;
391             list = null;
392             arrow = null;
393             break;
394         case SWT.FocusIn:
395             Control focusControl = getDisplay ().getFocusControl ();
396             if (focusControl is arrow || focusControl is list) return;
397             if (isDropped()) {
398                 list.setFocus();
399             } else {
400                 text.setFocus();
401             }
402             break;
403         case SWT.Move:
404             dropDown (false);
405             break;
406         case SWT.Resize:
407             internalLayout (false);
408             break;
409         default:
410     }
411 }
412 
413 public override Point computeSize (int wHint, int hHint, bool changed) {
414     checkWidget ();
415     int width = 0, height = 0;
416     String[] items = list.getItems ();
417     GC gc = new GC (text);
418     int spacer = gc.stringExtent (" ").x; //$NON-NLS-1$
419     int textWidth = gc.stringExtent (text.getText ()).x;
420     for (int i = 0; i < items.length; i++) {
421         textWidth = Math.max (gc.stringExtent (items[i]).x, textWidth);
422     }
423     gc.dispose ();
424     Point textSize = text.computeSize (SWT.DEFAULT, SWT.DEFAULT, changed);
425     Point arrowSize = arrow.computeSize (SWT.DEFAULT, SWT.DEFAULT, changed);
426     Point listSize = list.computeSize (SWT.DEFAULT, SWT.DEFAULT, changed);
427     int borderWidth = getBorderWidth ();
428 
429     height = Math.max (textSize.y, arrowSize.y);
430     width = Math.max (textWidth + 2*spacer + arrowSize.x + 2*borderWidth, listSize.x);
431     if (wHint !is SWT.DEFAULT) width = wHint;
432     if (hHint !is SWT.DEFAULT) height = hHint;
433     return new Point (width + 2*borderWidth, height + 2*borderWidth);
434 }
435 /**
436  * Copies the selected text.
437  * <p>
438  * The current selection is copied to the clipboard.
439  * </p>
440  *
441  * @exception SWTException <ul>
442  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
443  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
444  * </ul>
445  *
446  * @since 3.3
447  */
448 public void copy () {
449     checkWidget ();
450     text.copy ();
451 }
452 void createPopup(String[] items, int selectionIndex) {
453     // create shell and list
454     popup = new Shell (getShell (), SWT.NO_TRIM | SWT.ON_TOP);
455     int style = getStyle ();
456     int listStyle = SWT.SINGLE | SWT.V_SCROLL;
457     if ((style & SWT.FLAT) !is 0) listStyle |= SWT.FLAT;
458     if ((style & SWT.RIGHT_TO_LEFT) !is 0) listStyle |= SWT.RIGHT_TO_LEFT;
459     if ((style & SWT.LEFT_TO_RIGHT) !is 0) listStyle |= SWT.LEFT_TO_RIGHT;
460     list = new List (popup, listStyle);
461     if (font !is null) list.setFont (font);
462     if (foreground !is null) list.setForeground (foreground);
463     if (background !is null) list.setBackground (background);
464 
465     int [] popupEvents = [SWT.Close, SWT.Paint, SWT.Deactivate];
466     for (int i=0; i<popupEvents.length; i++) popup.addListener (popupEvents [i], listener);
467     int [] listEvents = [SWT.MouseUp, SWT.Selection, SWT.Traverse, SWT.KeyDown, SWT.KeyUp, SWT.FocusIn, SWT.Dispose];
468     for (int i=0; i<listEvents.length; i++) list.addListener (listEvents [i], listener);
469 
470     if (items !is null) list.setItems (items);
471     if (selectionIndex !is -1) list.setSelection (selectionIndex);
472 }
473 /**
474  * Cuts the selected text.
475  * <p>
476  * The current selection is first copied to the
477  * clipboard and then deleted from the widget.
478  * </p>
479  *
480  * @exception SWTException <ul>
481  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
482  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
483  * </ul>
484  *
485  * @since 3.3
486  */
487 public void cut () {
488     checkWidget ();
489     text.cut ();
490 }
491 /**
492  * Deselects the item at the given zero-relative index in the receiver's
493  * list.  If the item at the index was already deselected, it remains
494  * deselected. Indices that are out of range are ignored.
495  *
496  * @param index the index of the item to deselect
497  *
498  * @exception SWTException <ul>
499  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
500  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
501  * </ul>
502  */
503 public void deselect (int index) {
504     checkWidget ();
505     if (0 <= index && index < list.getItemCount () &&
506             index is list.getSelectionIndex() &&
507             text.getText().equals(list.getItem(index))) {
508         text.setText("");  //$NON-NLS-1$
509         list.deselect (index);
510     }
511 }
512 /**
513  * Deselects all selected items in the receiver's list.
514  * <p>
515  * Note: To clear the selection in the receiver's text field,
516  * use <code>clearSelection()</code>.
517  * </p>
518  *
519  * @exception SWTException <ul>
520  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
521  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
522  * </ul>
523  *
524  * @see #clearSelection
525  */
526 public void deselectAll () {
527     checkWidget ();
528     text.setText("");  //$NON-NLS-1$
529     list.deselectAll ();
530 }
531 void dropDown (bool drop) {
532     if (drop is isDropped ()) return;
533     if (!drop) {
534         popup.setVisible (false);
535         if (!isDisposed () && isFocusControl()) {
536             text.setFocus();
537         }
538         return;
539     }
540     if (!isVisible()) return;
541     if (getShell() !is popup.getParent ()) {
542         String[] items = list.getItems ();
543         int selectionIndex = list.getSelectionIndex ();
544         list.removeListener (SWT.Dispose, listener);
545         popup.dispose();
546         popup = null;
547         list = null;
548         createPopup (items, selectionIndex);
549     }
550 
551     Point size = getSize ();
552     int itemCount = list.getItemCount ();
553     itemCount = (itemCount is 0) ? visibleItemCount : Math.min(visibleItemCount, itemCount);
554     int itemHeight = list.getItemHeight () * itemCount;
555     Point listSize = list.computeSize (SWT.DEFAULT, itemHeight, false);
556     list.setBounds (1, 1, Math.max (size.x - 2, listSize.x), listSize.y);
557 
558     int index = list.getSelectionIndex ();
559     if (index !is -1) list.setTopIndex (index);
560     Display display = getDisplay ();
561     Rectangle listRect = list.getBounds ();
562     Rectangle parentRect = display.map (getParent (), null, getBounds ());
563     Point comboSize = getSize ();
564     Rectangle displayRect = getMonitor ().getClientArea ();
565     int width = Math.max (comboSize.x, listRect.width + 2);
566     int height = listRect.height + 2;
567     int x = parentRect.x;
568     int y = parentRect.y + comboSize.y;
569     if (y + height > displayRect.y + displayRect.height) y = parentRect.y - height;
570     if (x + width > displayRect.x + displayRect.width) x = displayRect.x + displayRect.width - listRect.width;
571     popup.setBounds (x, y, width, height);
572     popup.setVisible (true);
573     if (isFocusControl()) list.setFocus ();
574 }
575 /*
576  * Return the lowercase of the first non-'&' character following
577  * an '&' character in the given string. If there are no '&'
578  * characters in the given string, return '\0'.
579  */
580 dchar _findMnemonic (String str) {
581     if (str is null) return '\0';
582     int index = 0;
583     auto length = str.length;
584     do {
585         while (index < length && str[index] !is '&') index++;
586         if (++index >= length) return '\0';
587         if (str[index] !is '&') return Character.toLowerCase( str.dcharAt(index) );
588         index++;
589     } while (index < length);
590     return '\0';
591 }
592 /*
593  * Return the Label immediately preceding the receiver in the z-order,
594  * or null if none.
595  */
596 Label getAssociatedLabel () {
597     Control[] siblings = getParent ().getChildren ();
598     for (int i = 0; i < siblings.length; i++) {
599         if (siblings [i] is this) {
600             if (i > 0 && ( null !is cast(Label)siblings [i-1] )) {
601                 return cast(Label) siblings [i-1];
602             }
603         }
604     }
605     return null;
606 }
607 public override Control [] getChildren () {
608     checkWidget();
609     return new Control [0];
610 }
611 /**
612  * Gets the editable state.
613  *
614  * @return whether or not the receiver is editable
615  *
616  * @exception SWTException <ul>
617  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
618  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
619  * </ul>
620  *
621  * @since 3.0
622  */
623 public bool getEditable () {
624     checkWidget ();
625     return text.getEditable();
626 }
627 /**
628  * Returns the item at the given, zero-relative index in the
629  * receiver's list. Throws an exception if the index is out
630  * of range.
631  *
632  * @param index the index of the item to return
633  * @return the item at the given index
634  *
635  * @exception IllegalArgumentException <ul>
636  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
637  * </ul>
638  * @exception SWTException <ul>
639  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
640  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
641  * </ul>
642  */
643 public String getItem (int index) {
644     checkWidget();
645     return list.getItem (index);
646 }
647 /**
648  * Returns the number of items contained in the receiver's list.
649  *
650  * @return the number of items
651  *
652  * @exception SWTException <ul>
653  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
654  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
655  * </ul>
656  */
657 public int getItemCount () {
658     checkWidget ();
659     return list.getItemCount ();
660 }
661 /**
662  * Returns the height of the area which would be used to
663  * display <em>one</em> of the items in the receiver's list.
664  *
665  * @return the height of one item
666  *
667  * @exception SWTException <ul>
668  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
669  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
670  * </ul>
671  */
672 public int getItemHeight () {
673     checkWidget ();
674     return list.getItemHeight ();
675 }
676 /**
677  * Returns an array of <code>String</code>s which are the items
678  * in the receiver's list.
679  * <p>
680  * Note: This is not the actual structure used by the receiver
681  * to maintain its list of items, so modifying the array will
682  * not affect the receiver.
683  * </p>
684  *
685  * @return the items in the receiver's list
686  *
687  * @exception SWTException <ul>
688  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
689  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
690  * </ul>
691  */
692 public String [] getItems () {
693     checkWidget ();
694     return list.getItems ();
695 }
696 /**
697  * Returns <code>true</code> if the receiver's list is visible,
698  * and <code>false</code> otherwise.
699  * <p>
700  * If one of the receiver's ancestors is not visible or some
701  * other condition makes the receiver not visible, this method
702  * may still indicate that it is considered visible even though
703  * it may not actually be showing.
704  * </p>
705  *
706  * @return the receiver's list's visibility state
707  *
708  * @exception SWTException <ul>
709  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
710  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
711  * </ul>
712  *
713  * @since 3.4
714  */
715 public bool getListVisible () {
716     checkWidget ();
717     return isDropped();
718 }
719 public override Menu getMenu() {
720     return text.getMenu();
721 }
722 /**
723  * Returns a <code>Point</code> whose x coordinate is the start
724  * of the selection in the receiver's text field, and whose y
725  * coordinate is the end of the selection. The returned values
726  * are zero-relative. An "empty" selection as indicated by
727  * the the x and y coordinates having the same value.
728  *
729  * @return a point representing the selection start and end
730  *
731  * @exception SWTException <ul>
732  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
733  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
734  * </ul>
735  */
736 public Point getSelection () {
737     checkWidget ();
738     return text.getSelection ();
739 }
740 /**
741  * Returns the zero-relative index of the item which is currently
742  * selected in the receiver's list, or -1 if no item is selected.
743  *
744  * @return the index of the selected item
745  *
746  * @exception SWTException <ul>
747  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
748  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
749  * </ul>
750  */
751 public int getSelectionIndex () {
752     checkWidget ();
753     return list.getSelectionIndex ();
754 }
755 public override int getStyle () {
756     int style = super.getStyle ();
757     style &= ~SWT.READ_ONLY;
758     if (!text.getEditable()) style |= SWT.READ_ONLY;
759     return style;
760 }
761 /**
762  * Returns a string containing a copy of the contents of the
763  * receiver's text field.
764  *
765  * @return the receiver's text
766  *
767  * @exception SWTException <ul>
768  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
769  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
770  * </ul>
771  */
772 public String getText () {
773     checkWidget ();
774     return text.getText ();
775 }
776 /**
777  * Returns the height of the receivers's text field.
778  *
779  * @return the text height
780  *
781  * @exception SWTException <ul>
782  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
783  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
784  * </ul>
785  */
786 public int getTextHeight () {
787     checkWidget ();
788     return text.getLineHeight ();
789 }
790 /**
791  * Returns the maximum number of characters that the receiver's
792  * text field is capable of holding. If this has not been changed
793  * by <code>setTextLimit()</code>, it will be the constant
794  * <code>Combo.LIMIT</code>.
795  *
796  * @return the text limit
797  *
798  * @exception SWTException <ul>
799  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
800  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
801  * </ul>
802  */
803 public int getTextLimit () {
804     checkWidget ();
805     return text.getTextLimit ();
806 }
807 /**
808  * Gets the number of items that are visible in the drop
809  * down portion of the receiver's list.
810  *
811  * @return the number of items that are visible
812  *
813  * @exception SWTException <ul>
814  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
815  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
816  * </ul>
817  *
818  * @since 3.0
819  */
820 public int getVisibleItemCount () {
821     checkWidget ();
822     return visibleItemCount;
823 }
824 void handleFocus (int type) {
825     if (isDisposed ()) return;
826     switch (type) {
827         case SWT.FocusIn: {
828             if (hasFocus) return;
829             if (getEditable ()) text.selectAll ();
830             hasFocus = true;
831             Shell shell = getShell ();
832             shell.removeListener (SWT.Deactivate, listener);
833             shell.addListener (SWT.Deactivate, listener);
834             Display display = getDisplay ();
835             display.removeFilter (SWT.FocusIn, filter);
836             display.addFilter (SWT.FocusIn, filter);
837             Event e = new Event ();
838             notifyListeners (SWT.FocusIn, e);
839             break;
840         }
841         case SWT.FocusOut: {
842             if (!hasFocus) return;
843             Control focusControl = getDisplay ().getFocusControl ();
844             if (focusControl is arrow || focusControl is list || focusControl is text) return;
845             hasFocus = false;
846             Shell shell = getShell ();
847             shell.removeListener(SWT.Deactivate, listener);
848             Display display = getDisplay ();
849             display.removeFilter (SWT.FocusIn, filter);
850             Event e = new Event ();
851             notifyListeners (SWT.FocusOut, e);
852             break;
853         }
854         default:
855     }
856 }
857 /**
858  * Searches the receiver's list starting at the first item
859  * (index 0) until an item is found that is equal to the
860  * argument, and returns the index of that item. If no item
861  * is found, returns -1.
862  *
863  * @param string the search item
864  * @return the index of the item
865  *
866  * @exception SWTException <ul>
867  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
868  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
869  * </ul>
870  */
871 public int indexOf (String string) {
872     checkWidget ();
873     // SWT extension: allow null for zero length string
874     //if (string is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
875     return list.indexOf (string);
876 }
877 /**
878  * Searches the receiver's list starting at the given,
879  * zero-relative index until an item is found that is equal
880  * to the argument, and returns the index of that item. If
881  * no item is found or the starting index is out of range,
882  * returns -1.
883  *
884  * @param string the search item
885  * @param start the zero-relative index at which to begin the search
886  * @return the index of the item
887  *
888  * @exception SWTException <ul>
889  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
890  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
891  * </ul>
892  */
893 public int indexOf (String string, int start) {
894     checkWidget ();
895     // SWT extension: allow null for zero length string
896     //if (string is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
897     return list.indexOf (string, start);
898 }
899 
900 void initAccessible() {
901     AccessibleAdapter accessibleAdapter = new class() AccessibleAdapter {
902         override
903         public void getName (AccessibleEvent e) {
904             String name = null;
905             Label label = getAssociatedLabel ();
906             if (label !is null) {
907                 name = stripMnemonic (label.getText());
908             }
909             e.result = name;
910         }
911         override
912         public void getKeyboardShortcut(AccessibleEvent e) {
913             String shortcut = null;
914             Label label = getAssociatedLabel ();
915             if (label !is null) {
916                 String text = label.getText ();
917                 if (text !is null) {
918                     dchar mnemonic = _findMnemonic (text);
919                     if (mnemonic !is '\0') {
920                         shortcut = Format( "Alt+{}", mnemonic ); //$NON-NLS-1$
921                     }
922                 }
923             }
924             e.result = shortcut;
925         }
926         override
927         public void getHelp (AccessibleEvent e) {
928             e.result = getToolTipText ();
929         }
930     };
931     getAccessible ().addAccessibleListener (accessibleAdapter);
932     text.getAccessible ().addAccessibleListener (accessibleAdapter);
933     list.getAccessible ().addAccessibleListener (accessibleAdapter);
934 
935     arrow.getAccessible ().addAccessibleListener (new class() AccessibleAdapter {
936         override
937         public void getName (AccessibleEvent e) {
938             e.result = isDropped () ? SWT.getMessage ("SWT_Close") : SWT.getMessage ("SWT_Open"); //$NON-NLS-1$ //$NON-NLS-2$
939         }
940         override
941         public void getKeyboardShortcut (AccessibleEvent e) {
942             e.result = "Alt+Down Arrow"; //$NON-NLS-1$
943         }
944         override
945         public void getHelp (AccessibleEvent e) {
946             e.result = getToolTipText ();
947         }
948     });
949 
950     getAccessible().addAccessibleTextListener (new class() AccessibleTextAdapter {
951         override
952         public void getCaretOffset (AccessibleTextEvent e) {
953             e.offset = text.getCaretPosition ();
954         }
955         override
956         public void getSelectionRange(AccessibleTextEvent e) {
957             Point sel = text.getSelection();
958             e.offset = sel.x;
959             e.length = sel.y - sel.x;
960         }
961     });
962 
963     getAccessible().addAccessibleControlListener (new class() AccessibleControlAdapter {
964         override
965         public void getChildAtPoint (AccessibleControlEvent e) {
966             Point testPoint = toControl (e.x, e.y);
967             if (getBounds ().contains (testPoint)) {
968                 e.childID = ACC.CHILDID_SELF;
969             }
970         }
971 
972         override
973         public void getLocation (AccessibleControlEvent e) {
974             Rectangle location = getBounds ();
975             Point pt = getParent().toDisplay (location.x, location.y);
976             e.x = pt.x;
977             e.y = pt.y;
978             e.width = location.width;
979             e.height = location.height;
980         }
981 
982         override
983         public void getChildCount (AccessibleControlEvent e) {
984             e.detail = 0;
985         }
986 
987         override
988         public void getRole (AccessibleControlEvent e) {
989             e.detail = ACC.ROLE_COMBOBOX;
990         }
991 
992         override
993         public void getState (AccessibleControlEvent e) {
994             e.detail = ACC.STATE_NORMAL;
995         }
996 
997         override
998         public void getValue (AccessibleControlEvent e) {
999             e.result = getText ();
1000         }
1001     });
1002 
1003     text.getAccessible ().addAccessibleControlListener (new class() AccessibleControlAdapter {
1004         override
1005         public void getRole (AccessibleControlEvent e) {
1006             e.detail = text.getEditable () ? ACC.ROLE_TEXT : ACC.ROLE_LABEL;
1007         }
1008     });
1009 
1010     arrow.getAccessible ().addAccessibleControlListener (new class() AccessibleControlAdapter {
1011         override
1012         public void getDefaultAction (AccessibleControlEvent e) {
1013             e.result = isDropped () ? SWT.getMessage ("SWT_Close") : SWT.getMessage ("SWT_Open"); //$NON-NLS-1$ //$NON-NLS-2$
1014         }
1015     });
1016 }
1017 bool isDropped () {
1018     return popup.getVisible ();
1019 }
1020 public override bool isFocusControl () {
1021     checkWidget();
1022     if (text.isFocusControl () || arrow.isFocusControl () || list.isFocusControl () || popup.isFocusControl ()) {
1023         return true;
1024     }
1025     return super.isFocusControl ();
1026 }
1027 void internalLayout (bool changed) {
1028     if (isDropped ()) dropDown (false);
1029     Rectangle rect = getClientArea ();
1030     int width = rect.width;
1031     int height = rect.height;
1032     Point arrowSize = arrow.computeSize (SWT.DEFAULT, height, changed);
1033     text.setBounds (0, 0, width - arrowSize.x, height);
1034     arrow.setBounds (width - arrowSize.x, 0, arrowSize.x, arrowSize.y);
1035 }
1036 void listEvent (Event event) {
1037     switch (event.type) {
1038         case SWT.Dispose:
1039             if (getShell () !is popup.getParent ()) {
1040                 String[] items = list.getItems ();
1041                 int selectionIndex = list.getSelectionIndex ();
1042                 popup = null;
1043                 list = null;
1044                 createPopup (items, selectionIndex);
1045             }
1046             break;
1047         case SWT.FocusIn: {
1048             handleFocus (SWT.FocusIn);
1049             break;
1050         }
1051         case SWT.MouseUp: {
1052             if (event.button !is 1) return;
1053             dropDown (false);
1054             break;
1055         }
1056         case SWT.Selection: {
1057             int index = list.getSelectionIndex ();
1058             if (index is -1) return;
1059             text.setText (list.getItem (index));
1060             text.selectAll ();
1061             list.setSelection (index);
1062             Event e = new Event ();
1063             e.time = event.time;
1064             e.stateMask = event.stateMask;
1065             e.doit = event.doit;
1066             notifyListeners (SWT.Selection, e);
1067             event.doit = e.doit;
1068             break;
1069         }
1070         case SWT.Traverse: {
1071             switch (event.detail) {
1072                 case SWT.TRAVERSE_RETURN:
1073                 case SWT.TRAVERSE_ESCAPE:
1074                 case SWT.TRAVERSE_ARROW_PREVIOUS:
1075                 case SWT.TRAVERSE_ARROW_NEXT:
1076                     event.doit = false;
1077                     break;
1078                 case SWT.TRAVERSE_TAB_NEXT:
1079                 case SWT.TRAVERSE_TAB_PREVIOUS:
1080                     event.doit = text.traverse(event.detail);
1081                     event.detail = SWT.TRAVERSE_NONE;
1082                     if (event.doit) dropDown(false);
1083                     return;
1084                 default:
1085             }
1086             Event e = new Event ();
1087             e.time = event.time;
1088             e.detail = event.detail;
1089             e.doit = event.doit;
1090             e.character = event.character;
1091             e.keyCode = event.keyCode;
1092             notifyListeners (SWT.Traverse, e);
1093             event.doit = e.doit;
1094             event.detail = e.detail;
1095             break;
1096         }
1097         case SWT.KeyUp: {
1098             Event e = new Event ();
1099             e.time = event.time;
1100             e.character = event.character;
1101             e.keyCode = event.keyCode;
1102             e.stateMask = event.stateMask;
1103             notifyListeners (SWT.KeyUp, e);
1104             break;
1105         }
1106         case SWT.KeyDown: {
1107             if (event.character is SWT.ESC) {
1108                 // Escape key cancels popup list
1109                 dropDown (false);
1110             }
1111             if ((event.stateMask & SWT.ALT) !is 0 && (event.keyCode is SWT.ARROW_UP || event.keyCode is SWT.ARROW_DOWN)) {
1112                 dropDown (false);
1113             }
1114             if (event.character is SWT.CR) {
1115                 // Enter causes default selection
1116                 dropDown (false);
1117                 Event e = new Event ();
1118                 e.time = event.time;
1119                 e.stateMask = event.stateMask;
1120                 notifyListeners (SWT.DefaultSelection, e);
1121             }
1122             // At this point the widget may have been disposed.
1123             // If so, do not continue.
1124             if (isDisposed ()) break;
1125             Event e = new Event();
1126             e.time = event.time;
1127             e.character = event.character;
1128             e.keyCode = event.keyCode;
1129             e.stateMask = event.stateMask;
1130             notifyListeners(SWT.KeyDown, e);
1131             break;
1132 
1133         }
1134         default:
1135     }
1136 }
1137 /**
1138  * Pastes text from clipboard.
1139  * <p>
1140  * The selected text is deleted from the widget
1141  * and new text inserted from the clipboard.
1142  * </p>
1143  *
1144  * @exception SWTException <ul>
1145  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1146  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1147  * </ul>
1148  *
1149  * @since 3.3
1150  */
1151 public void paste () {
1152     checkWidget ();
1153     text.paste ();
1154 }
1155 void popupEvent(Event event) {
1156     switch (event.type) {
1157         case SWT.Paint:
1158             // draw black rectangle around list
1159             Rectangle listRect = list.getBounds();
1160             Color black = getDisplay().getSystemColor(SWT.COLOR_BLACK);
1161             event.gc.setForeground(black);
1162             event.gc.drawRectangle(0, 0, listRect.width + 1, listRect.height + 1);
1163             break;
1164         case SWT.Close:
1165             event.doit = false;
1166             dropDown (false);
1167             break;
1168         case SWT.Deactivate:
1169             /*
1170              * Bug in GTK. When the arrow button is pressed the popup control receives a
1171              * deactivate event and then the arrow button receives a selection event. If
1172              * we hide the popup in the deactivate event, the selection event will show
1173              * it again. To prevent the popup from showing again, we will let the selection
1174              * event of the arrow button hide the popup.
1175              * In Windows, hiding the popup during the deactivate causes the deactivate
1176              * to be called twice and the selection event to be disappear.
1177              */
1178             if ("carbon" != (SWT.getPlatform())) {
1179                 Point point = arrow.toControl(getDisplay().getCursorLocation());
1180                 Point size = arrow.getSize();
1181                 Rectangle rect = new Rectangle(0, 0, size.x, size.y);
1182                 if (!rect.contains(point)) dropDown (false);
1183             } else {
1184                 dropDown(false);
1185             }
1186             break;
1187         default:
1188     }
1189 }
1190 public override void redraw () {
1191     super.redraw();
1192     text.redraw();
1193     arrow.redraw();
1194     if (popup.isVisible()) list.redraw();
1195 }
1196 public override void redraw (int x, int y, int width, int height, bool all) {
1197     super.redraw(x, y, width, height, true);
1198 }
1199 
1200 /**
1201  * Removes the item from the receiver's list at the given
1202  * zero-relative index.
1203  *
1204  * @param index the index for the item
1205  *
1206  * @exception IllegalArgumentException <ul>
1207  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1208  * </ul>
1209  * @exception SWTException <ul>
1210  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1211  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1212  * </ul>
1213  */
1214 public void remove (int index) {
1215     checkWidget();
1216     list.remove (index);
1217 }
1218 /**
1219  * Removes the items from the receiver's list which are
1220  * between the given zero-relative start and end
1221  * indices (inclusive).
1222  *
1223  * @param start the start of the range
1224  * @param end the end of the range
1225  *
1226  * @exception IllegalArgumentException <ul>
1227  *    <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>
1228  * </ul>
1229  * @exception SWTException <ul>
1230  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1231  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1232  * </ul>
1233  */
1234 public void remove (int start, int end) {
1235     checkWidget();
1236     list.remove (start, end);
1237 }
1238 /**
1239  * Searches the receiver's list starting at the first item
1240  * until an item is found that is equal to the argument,
1241  * and removes that item from the list.
1242  *
1243  * @param string the item to remove
1244  *
1245  * @exception IllegalArgumentException <ul>
1246  *    <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li>
1247  * </ul>
1248  * @exception SWTException <ul>
1249  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1250  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1251  * </ul>
1252  */
1253 public void remove (String string) {
1254     checkWidget();
1255     // SWT extension: allow null for zero length string
1256     //if (string is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1257     list.remove (string);
1258 }
1259 /**
1260  * Removes all of the items from the receiver's list and clear the
1261  * contents of receiver's text field.
1262  * <p>
1263  * @exception SWTException <ul>
1264  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1265  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1266  * </ul>
1267  */
1268 public void removeAll () {
1269     checkWidget();
1270     text.setText (""); //$NON-NLS-1$
1271     list.removeAll ();
1272 }
1273 /**
1274  * Removes the listener from the collection of listeners who will
1275  * be notified when the receiver's text is modified.
1276  *
1277  * @param listener the listener which should no longer be notified
1278  *
1279  * @exception IllegalArgumentException <ul>
1280  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1281  * </ul>
1282  * @exception SWTException <ul>
1283  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1284  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1285  * </ul>
1286  *
1287  * @see ModifyListener
1288  * @see #addModifyListener
1289  */
1290 public void removeModifyListener (ModifyListener listener) {
1291     checkWidget();
1292     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1293     removeListener(SWT.Modify, listener);
1294 }
1295 /**
1296  * Removes the listener from the collection of listeners who will
1297  * be notified when the user changes the receiver's selection.
1298  *
1299  * @param listener the listener which should no longer be notified
1300  *
1301  * @exception IllegalArgumentException <ul>
1302  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1303  * </ul>
1304  * @exception SWTException <ul>
1305  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1306  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1307  * </ul>
1308  *
1309  * @see SelectionListener
1310  * @see #addSelectionListener
1311  */
1312 public void removeSelectionListener (SelectionListener listener) {
1313     checkWidget();
1314     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1315     removeListener(SWT.Selection, listener);
1316     removeListener(SWT.DefaultSelection,listener);
1317 }
1318 /**
1319  * Removes the listener from the collection of listeners who will
1320  * be notified when the control is verified.
1321  *
1322  * @param listener the listener which should no longer be notified
1323  *
1324  * @exception IllegalArgumentException <ul>
1325  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
1326  * </ul>
1327  * @exception SWTException <ul>
1328  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1329  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1330  * </ul>
1331  *
1332  * @see VerifyListener
1333  * @see #addVerifyListener
1334  *
1335  * @since 3.3
1336  */
1337 public void removeVerifyListener (VerifyListener listener) {
1338     checkWidget();
1339     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1340     removeListener(SWT.Verify, listener);
1341 }
1342 /**
1343  * Selects the item at the given zero-relative index in the receiver's
1344  * list.  If the item at the index was already selected, it remains
1345  * selected. Indices that are out of range are ignored.
1346  *
1347  * @param index the index of the item to select
1348  *
1349  * @exception SWTException <ul>
1350  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1351  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1352  * </ul>
1353  */
1354 public void select (int index) {
1355     checkWidget();
1356     if (index is -1) {
1357         list.deselectAll ();
1358         text.setText (""); //$NON-NLS-1$
1359         return;
1360     }
1361     if (0 <= index && index < list.getItemCount()) {
1362         if (index !is getSelectionIndex()) {
1363             text.setText (list.getItem (index));
1364             text.selectAll ();
1365             list.select (index);
1366             list.showSelection ();
1367         }
1368     }
1369 }
1370 public override void setBackground (Color color) {
1371     super.setBackground(color);
1372     background = color;
1373     if (text !is null) text.setBackground(color);
1374     if (list !is null) list.setBackground(color);
1375     if (arrow !is null) arrow.setBackground(color);
1376 }
1377 /**
1378  * Sets the editable state.
1379  *
1380  * @param editable the new editable state
1381  *
1382  * @exception SWTException <ul>
1383  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1384  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1385  * </ul>
1386  *
1387  * @since 3.0
1388  */
1389 public void setEditable (bool editable) {
1390     checkWidget ();
1391     text.setEditable(editable);
1392 }
1393 public override void setEnabled (bool enabled) {
1394     super.setEnabled(enabled);
1395     if (popup !is null) popup.setVisible (false);
1396     if (text !is null) text.setEnabled(enabled);
1397     if (arrow !is null) arrow.setEnabled(enabled);
1398 }
1399 public override bool setFocus () {
1400     checkWidget();
1401     if (!isEnabled () || !isVisible ()) return false;
1402     if (isFocusControl ()) return true;
1403     return text.setFocus ();
1404 }
1405 public override void setFont (Font font) {
1406     super.setFont (font);
1407     this.font = font;
1408     text.setFont (font);
1409     list.setFont (font);
1410     internalLayout (true);
1411 }
1412 public override void setForeground (Color color) {
1413     super.setForeground(color);
1414     foreground = color;
1415     if (text !is null) text.setForeground(color);
1416     if (list !is null) list.setForeground(color);
1417     if (arrow !is null) arrow.setForeground(color);
1418 }
1419 /**
1420  * Sets the text of the item in the receiver's list at the given
1421  * zero-relative index to the string argument. This is equivalent
1422  * to <code>remove</code>'ing the old item at the index, and then
1423  * <code>add</code>'ing the new item at that index.
1424  *
1425  * @param index the index for the item
1426  * @param string the new text for the item
1427  *
1428  * @exception IllegalArgumentException <ul>
1429  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1430  * </ul>
1431  * @exception SWTException <ul>
1432  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1433  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1434  * </ul>
1435  */
1436 public void setItem (int index, String string) {
1437     checkWidget();
1438     list.setItem (index, string);
1439 }
1440 /**
1441  * Sets the receiver's list to be the given array of items.
1442  *
1443  * @param items the array of items
1444  *
1445  * @exception IllegalArgumentException <ul>
1446  *    <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li>
1447  * </ul>
1448  * @exception SWTException <ul>
1449  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1450  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1451  * </ul>
1452  */
1453 public void setItems (String [] items) {
1454     checkWidget ();
1455     list.setItems (items);
1456     if (!text.getEditable ()) text.setText (""); //$NON-NLS-1$
1457 }
1458 /**
1459  * Sets the layout which is associated with the receiver to be
1460  * the argument which may be null.
1461  * <p>
1462  * Note: No Layout can be set on this Control because it already
1463  * manages the size and position of its children.
1464  * </p>
1465  *
1466  * @param layout the receiver's new layout or null
1467  *
1468  * @exception SWTException <ul>
1469  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1470  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1471  * </ul>
1472  */
1473 public override void setLayout (Layout layout) {
1474     checkWidget ();
1475     return;
1476 }
1477 /**
1478  * Marks the receiver's list as visible if the argument is <code>true</code>,
1479  * and marks it invisible otherwise.
1480  * <p>
1481  * If one of the receiver's ancestors is not visible or some
1482  * other condition makes the receiver not visible, marking
1483  * it visible may not actually cause it to be displayed.
1484  * </p>
1485  *
1486  * @param visible the new visibility state
1487  *
1488  * @exception SWTException <ul>
1489  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1490  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1491  * </ul>
1492  *
1493  * @since 3.4
1494  */
1495 public void setListVisible (bool visible) {
1496     checkWidget ();
1497     dropDown(visible);
1498 }
1499 public override void setMenu(Menu menu) {
1500     text.setMenu(menu);
1501 }
1502 /**
1503  * Sets the selection in the receiver's text field to the
1504  * range specified by the argument whose x coordinate is the
1505  * start of the selection and whose y coordinate is the end
1506  * of the selection.
1507  *
1508  * @param selection a point representing the new selection start and end
1509  *
1510  * @exception IllegalArgumentException <ul>
1511  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1512  * </ul>
1513  * @exception SWTException <ul>
1514  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1515  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1516  * </ul>
1517  */
1518 public void setSelection (Point selection) {
1519     checkWidget();
1520     if (selection is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1521     text.setSelection (selection.x, selection.y);
1522 }
1523 
1524 /**
1525  * Sets the contents of the receiver's text field to the
1526  * given string.
1527  * <p>
1528  * Note: The text field in a <code>Combo</code> is typically
1529  * only capable of displaying a single line of text. Thus,
1530  * setting the text to a string containing line breaks or
1531  * other special characters will probably cause it to
1532  * display incorrectly.
1533  * </p>
1534  *
1535  * @param string the new text
1536  *
1537  * @exception SWTException <ul>
1538  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1539  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1540  * </ul>
1541  */
1542 public void setText (String string) {
1543     checkWidget();
1544     // SWT extension: allow null for zero length string
1545     //if (string is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
1546     int index = list.indexOf (string);
1547     if (index is -1) {
1548         list.deselectAll ();
1549         text.setText (string);
1550         return;
1551     }
1552     text.setText (string);
1553     text.selectAll ();
1554     list.setSelection (index);
1555     list.showSelection ();
1556 }
1557 /**
1558  * Sets the maximum number of characters that the receiver's
1559  * text field is capable of holding to be the argument.
1560  *
1561  * @param limit new text limit
1562  *
1563  * @exception IllegalArgumentException <ul>
1564  *    <li>ERROR_CANNOT_BE_ZERO - if the limit is zero</li>
1565  * </ul>
1566  * @exception SWTException <ul>
1567  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1568  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1569  * </ul>
1570  */
1571 public void setTextLimit (int limit) {
1572     checkWidget();
1573     text.setTextLimit (limit);
1574 }
1575 
1576 public override void setToolTipText (String string) {
1577     checkWidget();
1578     super.setToolTipText(string);
1579     arrow.setToolTipText (string);
1580     text.setToolTipText (string);
1581 }
1582 
1583 public override void setVisible (bool visible) {
1584     super.setVisible(visible);
1585     /*
1586      * At this point the widget may have been disposed in a FocusOut event.
1587      * If so then do not continue.
1588      */
1589     if (isDisposed ()) return;
1590     // TEMPORARY CODE
1591     if (popup is null || popup.isDisposed ()) return;
1592     if (!visible) popup.setVisible (false);
1593 }
1594 /**
1595  * Sets the number of items that are visible in the drop
1596  * down portion of the receiver's list.
1597  *
1598  * @param count the new number of items to be visible
1599  *
1600  * @exception SWTException <ul>
1601  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1602  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1603  * </ul>
1604  *
1605  * @since 3.0
1606  */
1607 public void setVisibleItemCount (int count) {
1608     checkWidget ();
1609     if (count < 0) return;
1610     visibleItemCount = count;
1611 }
1612 String stripMnemonic (String string) {
1613     int index = 0;
1614     int length_ = cast(int)/*64bit*/string.length;
1615     do {
1616         while ((index < length_) && (string[index] !is '&')) index++;
1617         if (++index >= length_) return string;
1618         if (string[index] !is '&') {
1619             return string[0 .. index-1] ~ string[index .. length_];
1620         }
1621         index++;
1622     } while (index < length_);
1623     return string;
1624 }
1625 void textEvent (Event event) {
1626     switch (event.type) {
1627         case SWT.FocusIn: {
1628             handleFocus (SWT.FocusIn);
1629             break;
1630         }
1631         case SWT.DefaultSelection: {
1632             dropDown (false);
1633             Event e = new Event ();
1634             e.time = event.time;
1635             e.stateMask = event.stateMask;
1636             notifyListeners (SWT.DefaultSelection, e);
1637             break;
1638         }
1639         case SWT.KeyDown: {
1640             Event keyEvent = new Event ();
1641             keyEvent.time = event.time;
1642             keyEvent.character = event.character;
1643             keyEvent.keyCode = event.keyCode;
1644             keyEvent.stateMask = event.stateMask;
1645             notifyListeners (SWT.KeyDown, keyEvent);
1646             if (isDisposed ()) break;
1647             event.doit = keyEvent.doit;
1648             if (!event.doit) break;
1649             if (event.keyCode is SWT.ARROW_UP || event.keyCode is SWT.ARROW_DOWN) {
1650                 event.doit = false;
1651                 if ((event.stateMask & SWT.ALT) !is 0) {
1652                     bool dropped = isDropped ();
1653                     text.selectAll ();
1654                     if (!dropped) setFocus ();
1655                     dropDown (!dropped);
1656                     break;
1657                 }
1658 
1659                 int oldIndex = getSelectionIndex ();
1660                 if (event.keyCode is SWT.ARROW_UP) {
1661                     select (Math.max (oldIndex - 1, 0));
1662                 } else {
1663                     select (Math.min (oldIndex + 1, getItemCount () - 1));
1664                 }
1665                 if (oldIndex !is getSelectionIndex ()) {
1666                     Event e = new Event();
1667                     e.time = event.time;
1668                     e.stateMask = event.stateMask;
1669                     notifyListeners (SWT.Selection, e);
1670                 }
1671                 if (isDisposed ()) break;
1672             }
1673 
1674             // Further work : Need to add support for incremental search in
1675             // pop up list as characters typed in text widget
1676             break;
1677         }
1678         case SWT.KeyUp: {
1679             Event e = new Event ();
1680             e.time = event.time;
1681             e.character = event.character;
1682             e.keyCode = event.keyCode;
1683             e.stateMask = event.stateMask;
1684             notifyListeners (SWT.KeyUp, e);
1685             event.doit = e.doit;
1686             break;
1687         }
1688         case SWT.MenuDetect: {
1689             Event e = new Event ();
1690             e.time = event.time;
1691             notifyListeners (SWT.MenuDetect, e);
1692             break;
1693         }
1694         case SWT.Modify: {
1695             list.deselectAll ();
1696             Event e = new Event ();
1697             e.time = event.time;
1698             notifyListeners (SWT.Modify, e);
1699             break;
1700         }
1701         case SWT.MouseDown: {
1702             Event mouseEvent = new Event ();
1703             mouseEvent.button = event.button;
1704             mouseEvent.count = event.count;
1705             mouseEvent.stateMask = event.stateMask;
1706             mouseEvent.time = event.time;
1707             mouseEvent.x = event.x; mouseEvent.y = event.y;
1708             notifyListeners (SWT.MouseDown, mouseEvent);
1709             if (isDisposed ()) break;
1710             event.doit = mouseEvent.doit;
1711             if (!event.doit) break;
1712             if (event.button !is 1) return;
1713             if (text.getEditable ()) return;
1714             bool dropped = isDropped ();
1715             text.selectAll ();
1716             if (!dropped) setFocus ();
1717             dropDown (!dropped);
1718             break;
1719         }
1720         case SWT.MouseUp: {
1721             Event mouseEvent = new Event ();
1722             mouseEvent.button = event.button;
1723             mouseEvent.count = event.count;
1724             mouseEvent.stateMask = event.stateMask;
1725             mouseEvent.time = event.time;
1726             mouseEvent.x = event.x; mouseEvent.y = event.y;
1727             notifyListeners (SWT.MouseUp, mouseEvent);
1728             if (isDisposed ()) break;
1729             event.doit = mouseEvent.doit;
1730             if (!event.doit) break;
1731             if (event.button !is 1) return;
1732             if (text.getEditable ()) return;
1733             text.selectAll ();
1734             break;
1735         }
1736         case SWT.MouseDoubleClick: {
1737             Event mouseEvent = new Event ();
1738             mouseEvent.button = event.button;
1739             mouseEvent.count = event.count;
1740             mouseEvent.stateMask = event.stateMask;
1741             mouseEvent.time = event.time;
1742             mouseEvent.x = event.x; mouseEvent.y = event.y;
1743             notifyListeners (SWT.MouseDoubleClick, mouseEvent);
1744             break;
1745         }
1746         case SWT.MouseWheel: {
1747             Event keyEvent = new Event ();
1748             keyEvent.time = event.time;
1749             keyEvent.keyCode = event.count > 0 ? SWT.ARROW_UP : SWT.ARROW_DOWN;
1750             keyEvent.stateMask = event.stateMask;
1751             notifyListeners (SWT.KeyDown, keyEvent);
1752             if (isDisposed ()) break;
1753             event.doit = keyEvent.doit;
1754             if (!event.doit) break;
1755             if (event.count !is 0) {
1756                 event.doit = false;
1757                 int oldIndex = getSelectionIndex ();
1758                 if (event.count > 0) {
1759                     select (Math.max (oldIndex - 1, 0));
1760                 } else {
1761                     select (Math.min (oldIndex + 1, getItemCount () - 1));
1762                 }
1763                 if (oldIndex !is getSelectionIndex ()) {
1764                     Event e = new Event();
1765                     e.time = event.time;
1766                     e.stateMask = event.stateMask;
1767                     notifyListeners (SWT.Selection, e);
1768                 }
1769                 if (isDisposed ()) break;
1770             }
1771             break;
1772         }
1773         case SWT.Traverse: {
1774             switch (event.detail) {
1775                 case SWT.TRAVERSE_ARROW_PREVIOUS:
1776                 case SWT.TRAVERSE_ARROW_NEXT:
1777                     // The enter causes default selection and
1778                     // the arrow keys are used to manipulate the list contents so
1779                     // do not use them for traversal.
1780                     event.doit = false;
1781                     break;
1782                 case SWT.TRAVERSE_TAB_PREVIOUS:
1783                     event.doit = traverse(SWT.TRAVERSE_TAB_PREVIOUS);
1784                     event.detail = SWT.TRAVERSE_NONE;
1785                     return;
1786                 default:
1787             }
1788             Event e = new Event ();
1789             e.time = event.time;
1790             e.detail = event.detail;
1791             e.doit = event.doit;
1792             e.character = event.character;
1793             e.keyCode = event.keyCode;
1794             notifyListeners (SWT.Traverse, e);
1795             event.doit = e.doit;
1796             event.detail = e.detail;
1797             break;
1798         }
1799         case SWT.Verify: {
1800             Event e = new Event ();
1801             e.text = event.text;
1802             e.start = event.start;
1803             e.end = event.end;
1804             e.character = event.character;
1805             e.keyCode = event.keyCode;
1806             e.stateMask = event.stateMask;
1807             notifyListeners (SWT.Verify, e);
1808             event.doit = e.doit;
1809             break;
1810         }
1811         default:
1812     }
1813 }
1814 }