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 &lt; 3; i++) {
42 *       TreeItem item = new TreeItem(tree, SWT.NONE);
43 *       item.setText("item " + i);
44 *       for (int j = 0; j &lt; 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 }