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.TreeEditor; 14 15 import java.lang.all; 16 17 18 import org.eclipse.swt.SWT; 19 import org.eclipse.swt.events.ControlEvent; 20 import org.eclipse.swt.events.ControlListener; 21 import org.eclipse.swt.events.TreeEvent; 22 import org.eclipse.swt.events.TreeListener; 23 import org.eclipse.swt.graphics.Rectangle; 24 import org.eclipse.swt.widgets.Control; 25 import org.eclipse.swt.widgets.Display; 26 import org.eclipse.swt.widgets.Tree; 27 import org.eclipse.swt.widgets.TreeColumn; 28 import org.eclipse.swt.widgets.TreeItem; 29 import org.eclipse.swt.custom.ControlEditor; 30 31 /** 32 * 33 * A TreeEditor is a manager for a Control that appears above a cell in a Tree and tracks with the 34 * moving and resizing of that cell. It can be used to display a text widget above a cell 35 * in a Tree so that the user can edit the contents of that cell. It can also be used to display 36 * a button that can launch a dialog for modifying the contents of the associated cell. 37 * 38 * <p> Here is an example of using a TreeEditor: 39 * <code><pre> 40 * final Tree tree = new Tree(shell, SWT.BORDER); 41 * for (int i = 0; i < 3; i++) { 42 * TreeItem item = new TreeItem(tree, SWT.NONE); 43 * item.setText("item " + i); 44 * for (int j = 0; j < 3; j++) { 45 * TreeItem subItem = new TreeItem(item, SWT.NONE); 46 * subItem.setText("item " + i + " " + j); 47 * } 48 * } 49 * 50 * final TreeEditor editor = new TreeEditor(tree); 51 * //The editor must have the same size as the cell and must 52 * //not be any smaller than 50 pixels. 53 * editor.horizontalAlignment = SWT.LEFT; 54 * editor.grabHorizontal = true; 55 * editor.minimumWidth = 50; 56 * 57 * tree.addSelectionListener(new SelectionAdapter() { 58 * public void widgetSelected(SelectionEvent e) { 59 * // Clean up any previous editor control 60 * Control oldEditor = editor.getEditor(); 61 * if (oldEditor !is null) oldEditor.dispose(); 62 * 63 * // Identify the selected row 64 * TreeItem item = (TreeItem)e.item; 65 * if (item is null) return; 66 * 67 * // The control that will be the editor must be a child of the Tree 68 * Text newEditor = new Text(tree, SWT.NONE); 69 * newEditor.setText(item.getText()); 70 * newEditor.addModifyListener(new ModifyListener() { 71 * public void modifyText(ModifyEvent e) { 72 * Text text = (Text)editor.getEditor(); 73 * editor.getItem().setText(text.getText()); 74 * } 75 * }); 76 * newEditor.selectAll(); 77 * newEditor.setFocus(); 78 * editor.setEditor(newEditor, item); 79 * } 80 * }); 81 * </pre></code> 82 * 83 * @see <a href="http://www.eclipse.org/swt/snippets/#treeeditor">TreeEditor snippets</a> 84 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 85 */ 86 87 public class TreeEditor : ControlEditor { 88 Tree tree; 89 TreeItem item; 90 int column = 0; 91 ControlListener columnListener; 92 TreeListener treeListener; 93 Runnable timer; 94 static const int TIMEOUT = 1500; 95 96 /** 97 * Creates a TreeEditor for the specified Tree. 98 * 99 * @param tree the Tree Control above which this editor will be displayed 100 * 101 */ 102 public this (Tree tree) { 103 super(tree); 104 this.tree = tree; 105 106 columnListener = new class() ControlListener { 107 public void controlMoved(ControlEvent e){ 108 layout(); 109 } 110 public void controlResized(ControlEvent e){ 111 layout(); 112 } 113 }; 114 timer = new class() Runnable { 115 public void run() { 116 layout (); 117 } 118 }; 119 treeListener = new class() TreeListener { 120 Runnable runnable; 121 this(){ 122 runnable = new class() Runnable { 123 public void run() { 124 if (this.outer.outer.editor is null || this.outer.outer.editor.isDisposed()) return; 125 if (this.outer.outer.tree.isDisposed()) return; 126 layout(); 127 this.outer.outer.editor.setVisible(true); 128 } 129 }; 130 } 131 public void treeCollapsed(TreeEvent e) { 132 if (this.outer.editor is null || this.outer.editor.isDisposed ()) return; 133 this.outer.editor.setVisible(false); 134 e.display.asyncExec(runnable); 135 } 136 public void treeExpanded(TreeEvent e) { 137 if (this.outer.editor is null || this.outer.editor.isDisposed ()) return; 138 this.outer.editor.setVisible(false); 139 e.display.asyncExec(runnable); 140 } 141 }; 142 tree.addTreeListener(treeListener); 143 144 // To be consistent with older versions of SWT, grabVertical defaults to true 145 grabVertical = true; 146 } 147 148 override Rectangle computeBounds () { 149 if (item is null || column is -1 || item.isDisposed()) return new Rectangle(0, 0, 0, 0); 150 Rectangle cell = item.getBounds(column); 151 Rectangle rect = item.getImageBounds(column); 152 cell.x = rect.x + rect.width; 153 cell.width -= rect.width; 154 Rectangle area = tree.getClientArea(); 155 if (cell.x < area.x + area.width) { 156 if (cell.x + cell.width > area.x + area.width) { 157 cell.width = area.x + area.width - cell.x; 158 } 159 } 160 Rectangle editorRect = new Rectangle(cell.x, cell.y, minimumWidth, minimumHeight); 161 162 if (grabHorizontal) { 163 if (tree.getColumnCount() is 0) { 164 // Bounds of tree item only include the text area - stretch out to include 165 // entire client area 166 cell.width = area.x + area.width - cell.x; 167 } 168 editorRect.width = Math.max(cell.width, minimumWidth); 169 } 170 171 if (grabVertical) { 172 editorRect.height = Math.max(cell.height, minimumHeight); 173 } 174 175 if (horizontalAlignment is SWT.RIGHT) { 176 editorRect.x += cell.width - editorRect.width; 177 } else if (horizontalAlignment is SWT.LEFT) { 178 // do nothing - cell.x is the right answer 179 } else { // default is CENTER 180 editorRect.x += (cell.width - editorRect.width)/2; 181 } 182 // don't let the editor overlap with the + / - of the tree 183 editorRect.x = Math.max(cell.x, editorRect.x); 184 185 if (verticalAlignment is SWT.BOTTOM) { 186 editorRect.y += cell.height - editorRect.height; 187 } else if (verticalAlignment is SWT.TOP) { 188 // do nothing - cell.y is the right answer 189 } else { // default is CENTER 190 editorRect.y += (cell.height - editorRect.height)/2; 191 } 192 return editorRect; 193 } 194 195 /** 196 * Removes all associations between the TreeEditor and the row in the tree. The 197 * tree and the editor Control are <b>not</b> disposed. 198 */ 199 public override void dispose () { 200 if (tree !is null && !tree.isDisposed()) { 201 if (this.column > -1 && this.column < tree.getColumnCount()){ 202 TreeColumn treeColumn = tree.getColumn(this.column); 203 treeColumn.removeControlListener(columnListener); 204 } 205 if (treeListener !is null) tree.removeTreeListener(treeListener); 206 } 207 columnListener = null; 208 treeListener = null; 209 tree = null; 210 item = null; 211 column = 0; 212 timer = null; 213 super.dispose(); 214 } 215 216 /** 217 * Returns the zero based index of the column of the cell being tracked by this editor. 218 * 219 * @return the zero based index of the column of the cell being tracked by this editor 220 * 221 * @since 3.1 222 */ 223 public int getColumn () { 224 return column; 225 } 226 227 /** 228 * Returns the TreeItem for the row of the cell being tracked by this editor. 229 * 230 * @return the TreeItem for the row of the cell being tracked by this editor 231 */ 232 public TreeItem getItem () { 233 return item; 234 } 235 236 void resize () { 237 layout(); 238 /* 239 * On some platforms, the table scrolls when an item that 240 * is partially visible at the bottom of the table is 241 * selected. Ensure that the correct row is edited by 242 * laying out one more time in a timerExec(). 243 */ 244 if (tree !is null) { 245 Display display = tree.getDisplay(); 246 display.timerExec(-1, timer); 247 display.timerExec(TIMEOUT, timer); 248 } 249 } 250 251 /** 252 * Sets the zero based index of the column of the cell being tracked by this editor. 253 * 254 * @param column the zero based index of the column of the cell being tracked by this editor 255 * 256 * @since 3.1 257 */ 258 public void setColumn(int column) { 259 int columnCount = tree.getColumnCount(); 260 // Separately handle the case where the tree has no TreeColumns. 261 // In this situation, there is a single default column. 262 if (columnCount is 0) { 263 this.column = (column is 0) ? 0 : -1; 264 resize(); 265 return; 266 } 267 if (this.column > -1 && this.column < columnCount){ 268 TreeColumn treeColumn = tree.getColumn(this.column); 269 treeColumn.removeControlListener(columnListener); 270 this.column = -1; 271 } 272 273 if (column < 0 || column >= tree.getColumnCount()) return; 274 275 this.column = column; 276 TreeColumn treeColumn = tree.getColumn(this.column); 277 treeColumn.addControlListener(columnListener); 278 resize(); 279 } 280 281 /** 282 * Specifies the <code>TreeItem</code> that is to be edited. 283 * 284 * @param item the item to be edited 285 */ 286 public void setItem (TreeItem item) { 287 this.item = item; 288 resize(); 289 } 290 291 /** 292 * Specify the Control that is to be displayed and the cell in the tree that it is to be positioned above. 293 * 294 * <p>Note: The Control provided as the editor <b>must</b> be created with its parent being the Tree control 295 * specified in the TreeEditor constructor. 296 * 297 * @param editor the Control that is displayed above the cell being edited 298 * @param item the TreeItem for the row of the cell being tracked by this editor 299 * @param column the zero based index of the column of the cell being tracked by this editor 300 * 301 * @since 3.1 302 */ 303 public void setEditor (Control editor, TreeItem item, int column) { 304 setItem(item); 305 setColumn(column); 306 setEditor(editor); 307 } 308 public override void setEditor (Control editor) { 309 super.setEditor(editor); 310 resize(); 311 } 312 313 /** 314 * Specify the Control that is to be displayed and the cell in the tree that it is to be positioned above. 315 * 316 * <p>Note: The Control provided as the editor <b>must</b> be created with its parent being the Tree control 317 * specified in the TreeEditor constructor. 318 * 319 * @param editor the Control that is displayed above the cell being edited 320 * @param item the TreeItem for the row of the cell being tracked by this editor 321 */ 322 public void setEditor (Control editor, TreeItem item) { 323 setItem(item); 324 setEditor(editor); 325 } 326 327 public override void layout () { 328 if (tree is null || tree.isDisposed()) return; 329 if (item is null || item.isDisposed()) return; 330 int columnCount = tree.getColumnCount(); 331 if (columnCount is 0 && column !is 0) return; 332 if (columnCount > 0 && (column < 0 || column >= columnCount)) return; 333 super.layout(); 334 } 335 336 }