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.PopupList; 14 15 import java.lang.all; 16 17 18 import org.eclipse.swt.SWT; 19 import org.eclipse.swt.SWTException; 20 import org.eclipse.swt.events.ControlEvent; 21 import org.eclipse.swt.events.ControlListener; 22 import org.eclipse.swt.events.KeyEvent; 23 import org.eclipse.swt.events.KeyListener; 24 import org.eclipse.swt.events.MouseEvent; 25 import org.eclipse.swt.events.MouseListener; 26 import org.eclipse.swt.graphics.Font; 27 import org.eclipse.swt.graphics.Point; 28 import org.eclipse.swt.graphics.Rectangle; 29 import org.eclipse.swt.widgets.Display; 30 import org.eclipse.swt.widgets.Event; 31 import org.eclipse.swt.widgets.List; 32 import org.eclipse.swt.widgets.Listener; 33 import org.eclipse.swt.widgets.Shell; 34 35 /** 36 * A PopupList is a list of selectable items that appears in its own shell positioned above 37 * its parent shell. It is used for selecting items when editing a Table cell (similar to the 38 * list that appears when you open a Combo box). 39 * 40 * The list will be positioned so that it does not run off the screen and the largest number of items 41 * are visible. It may appear above the current cursor location or below it depending how close you 42 * are to the edge of the screen. 43 * 44 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 45 */ 46 public class PopupList { 47 Shell shell; 48 List list; 49 int minimumWidth; 50 /** 51 * Creates a PopupList above the specified shell. 52 * 53 * @param parent a Shell control which will be the parent of the new instance (cannot be null) 54 */ 55 public this(Shell parent) { 56 this (parent, 0); 57 } 58 /** 59 * Creates a PopupList above the specified shell. 60 * 61 * @param parent a widget which will be the parent of the new instance (cannot be null) 62 * @param style the style of widget to construct 63 * 64 * @since 3.0 65 */ 66 public this(Shell parent, int style) { 67 shell = new Shell(parent, checkStyle(style)); 68 69 list = new List(shell, SWT.SINGLE | SWT.V_SCROLL); 70 71 // close dialog if user selects outside of the shell 72 shell.addListener(SWT.Deactivate, new class() Listener { 73 public void handleEvent(Event e){ 74 shell.setVisible (false); 75 } 76 }); 77 78 // resize shell when list resizes 79 shell.addControlListener(new class() ControlListener { 80 public void controlMoved(ControlEvent e){} 81 public void controlResized(ControlEvent e){ 82 Rectangle shellSize = shell.getClientArea(); 83 list.setSize(shellSize.width, shellSize.height); 84 } 85 }); 86 87 // return list selection on Mouse Up or Carriage Return 88 list.addMouseListener(new class() MouseListener { 89 public void mouseDoubleClick(MouseEvent e){} 90 public void mouseDown(MouseEvent e){} 91 public void mouseUp(MouseEvent e){ 92 shell.setVisible (false); 93 } 94 }); 95 list.addKeyListener(new class() KeyListener { 96 public void keyReleased(KeyEvent e){} 97 public void keyPressed(KeyEvent e){ 98 if (e.character is '\r'){ 99 shell.setVisible (false); 100 } 101 } 102 }); 103 104 } 105 private static int checkStyle (int style) { 106 int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; 107 return style & mask; 108 } 109 /** 110 * Gets the widget font. 111 * <p> 112 * @return the widget font 113 * 114 * @exception SWTException <ul> 115 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 116 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 117 * </ul> 118 */ 119 public Font getFont () { 120 return list.getFont(); 121 } 122 /** 123 * Gets the items. 124 * <p> 125 * This operation will fail if the items cannot 126 * be queried from the OS. 127 * 128 * @return the items in the widget 129 * 130 * @exception SWTException <ul> 131 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 132 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 133 * </ul> 134 */ 135 public String[] getItems () { 136 return list.getItems(); 137 } 138 /** 139 * Gets the minimum width of the list. 140 * 141 * @return the minimum width of the list 142 */ 143 public int getMinimumWidth () { 144 return minimumWidth; 145 } 146 /** 147 * Launches the Popup List, waits for an item to be selected and then closes the PopupList. 148 * 149 * @param rect the initial size and location of the PopupList; the dialog will be 150 * positioned so that it does not run off the screen and the largest number of items are visible 151 * 152 * @return the text of the selected item or null if no item is selected 153 */ 154 public String open (Rectangle rect) { 155 156 Point listSize = list.computeSize (rect.width, SWT.DEFAULT, false); 157 Rectangle screenSize = shell.getDisplay().getBounds(); 158 159 // Position the dialog so that it does not run off the screen and the largest number of items are visible 160 int spaceBelow = screenSize.height - (rect.y + rect.height) - 30; 161 int spaceAbove = rect.y - 30; 162 163 int y = 0; 164 if (spaceAbove > spaceBelow && listSize.y > spaceBelow) { 165 // place popup list above table cell 166 if (listSize.y > spaceAbove){ 167 listSize.y = spaceAbove; 168 } else { 169 listSize.y += 2; 170 } 171 y = rect.y - listSize.y; 172 173 } else { 174 // place popup list below table cell 175 if (listSize.y > spaceBelow){ 176 listSize.y = spaceBelow; 177 } else { 178 listSize.y += 2; 179 } 180 y = rect.y + rect.height; 181 } 182 183 // Make dialog as wide as the cell 184 listSize.x = rect.width; 185 // dialog width should not be less than minimumWidth 186 if (listSize.x < minimumWidth) 187 listSize.x = minimumWidth; 188 189 // Align right side of dialog with right side of cell 190 int x = rect.x + rect.width - listSize.x; 191 192 shell.setBounds(x, y, listSize.x, listSize.y); 193 194 shell.open(); 195 list.setFocus(); 196 197 Display display = shell.getDisplay(); 198 while (!shell.isDisposed () && shell.isVisible ()) { 199 if (!display.readAndDispatch()) display.sleep(); 200 } 201 202 String result = null; 203 if (!shell.isDisposed ()) { 204 String [] strings = list.getSelection (); 205 shell.dispose(); 206 if (strings.length !is 0) result = strings [0]; 207 } 208 return result; 209 } 210 /** 211 * Selects an item with text that starts with specified String. 212 * <p> 213 * If the item is not currently selected, it is selected. 214 * If the item at an index is selected, it remains selected. 215 * If the string is not matched, it is ignored. 216 * 217 * @param string the text of the item 218 * 219 * @exception SWTException <ul> 220 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 221 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 222 * </ul> 223 */ 224 public void select(String string) { 225 String[] items = list.getItems(); 226 227 // find the first entry in the list that starts with the 228 // specified string 229 if (string !is null){ 230 for (int i = 0; i < items.length; i++) { 231 if (items[i].startsWith(string)){ 232 int index = list.indexOf(items[i]); 233 list.select(index); 234 break; 235 } 236 } 237 } 238 } 239 /** 240 * Sets the widget font. 241 * <p> 242 * When new font is null, the font reverts 243 * to the default system font for the widget. 244 * 245 * @param font the new font (or null) 246 * 247 * @exception SWTException <ul> 248 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 249 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 250 * </ul> 251 */ 252 public void setFont (Font font) { 253 list.setFont(font); 254 } 255 /** 256 * Sets all items. 257 * <p> 258 * The previous selection is cleared. 259 * The previous items are deleted. 260 * The new items are added. 261 * The top index is set to 0. 262 * 263 * @param strings the array of items 264 * 265 * This operation will fail when an item is null 266 * or could not be added in the OS. 267 * 268 * @exception IllegalArgumentException <ul> 269 * <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li> 270 * </ul> 271 * @exception SWTException <ul> 272 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 273 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 274 * </ul> 275 */ 276 public void setItems (String[] strings) { 277 list.setItems(strings); 278 } 279 /** 280 * Sets the minimum width of the list. 281 * 282 * @param width the minimum width of the list 283 */ 284 public void setMinimumWidth (int width) { 285 if (width < 0) 286 SWT.error(SWT.ERROR_INVALID_ARGUMENT); 287 288 minimumWidth = width; 289 } 290 }