1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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.TableTree;
14 
15 
16 
17 import org.eclipse.swt.SWT;
18 import org.eclipse.swt.SWTException;
19 import org.eclipse.swt.events.SelectionEvent;
20 import org.eclipse.swt.events.SelectionListener;
21 import org.eclipse.swt.events.TreeListener;
22 import org.eclipse.swt.graphics.Color;
23 import org.eclipse.swt.graphics.Font;
24 import org.eclipse.swt.graphics.GC;
25 import org.eclipse.swt.graphics.Image;
26 import org.eclipse.swt.graphics.ImageData;
27 import org.eclipse.swt.graphics.PaletteData;
28 import org.eclipse.swt.graphics.Point;
29 import org.eclipse.swt.graphics.RGB;
30 import org.eclipse.swt.graphics.Rectangle;
31 import org.eclipse.swt.widgets.Composite;
32 import org.eclipse.swt.widgets.Event;
33 import org.eclipse.swt.widgets.Listener;
34 import org.eclipse.swt.widgets.Menu;
35 import org.eclipse.swt.widgets.Table;
36 import org.eclipse.swt.widgets.TableItem;
37 import org.eclipse.swt.widgets.TypedListener;
38 import org.eclipse.swt.custom.TableTreeItem;
39 import java.lang.all;
40 
41 /**
42  * A TableTree is a selectable user interface object
43  * that displays a hierarchy of items, and issues
44  * notification when an item is selected.
45  * A TableTree may be single or multi select.
46  * <p>
47  * The item children that may be added to instances of this class
48  * must be of type <code>TableTreeItem</code>.
49  * </p><p>
50  * Note that although this class is a subclass of <code>Composite</code>,
51  * it does not make sense to add <code>Control</code> children to it,
52  * or set a layout on it.
53  * </p><p>
54  * <dl>
55  *  <dt><b>Styles:</b> <dd> SINGLE, MULTI, CHECK, FULL_SELECTION
56  *  <dt><b>Events:</b> <dd> Selection, DefaultSelection, Collapse, Expand
57  * </dl>
58  * <p>
59  * Note: Only one of the styles SINGLE, and MULTI may be specified.
60  * </p>
61  *
62  * @deprecated As of 3.1 use Tree, TreeItem and TreeColumn
63  */
64 public class TableTree : Composite {
65 
66     alias Composite.computeSize computeSize;
67 
68     Table table;
69     TableTreeItem[] items;
70     Image plusImage, minusImage, sizeImage;
71 
72     /*
73     * TableTreeItems are not treated as children but rather as items.
74     * When the TableTree is disposed, all children are disposed because
75     * TableTree inherits this behaviour from Composite.  The items
76     * must be disposed separately.  Because TableTree is not part of
77     * the org.eclipse.swt.widgets module, the method releaseWidget can
78     * not be overridden (this is how items are disposed of in Table and Tree).
79     * Instead, the items are disposed of in response to the dispose event on the
80     * TableTree.  The "inDispose" flag is used to distinguish between disposing
81     * one TableTreeItem (e.g. when removing an entry from the TableTree) and
82     * disposing the entire TableTree.
83     */
84     bool inDispose = false;
85 
86     static /+const+/ TableTreeItem[] EMPTY_ITEMS;
87     static /+const+/ String[] EMPTY_TEXTS;
88     static /+const+/ Image[] EMPTY_IMAGES;
89     static /+const+/ String ITEMID = "TableTreeItemID"; //$NON-NLS-1$
90 
91 /**
92  * Constructs a new instance of this class given its parent
93  * and a style value describing its behavior and appearance.
94  * <p>
95  * The style value is either one of the style constants defined in
96  * class <code>SWT</code> which is applicable to instances of this
97  * class, or must be built by <em>bitwise OR</em>'ing together
98  * (that is, using the <code>int</code> "|" operator) two or more
99  * of those <code>SWT</code> style constants. The class description
100  * lists the style constants that are applicable to the class.
101  * Style bits are also inherited from superclasses.
102  * </p>
103  *
104  * @param parent a widget which will be the parent of the new instance (cannot be null)
105  * @param style the style of widget to construct
106  *
107  * @exception IllegalArgumentException <ul>
108  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
109  * </ul>
110  * @exception SWTException <ul>
111  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
112  * </ul>
113  *
114  * @see SWT#SINGLE
115  * @see SWT#MULTI
116  * @see SWT#CHECK
117  * @see SWT#FULL_SELECTION
118  * @see #getStyle
119  */
120 public this(Composite parent, int style) {
121     super(parent, checkStyle (style));
122     items = EMPTY_ITEMS;
123     table = new Table(this, style);
124     Listener tableListener = new class() Listener {
125         public void handleEvent(Event e) {
126             switch (e.type) {
127             case SWT.MouseDown: onMouseDown(e); break;
128             case SWT.Selection: onSelection(e); break;
129             case SWT.DefaultSelection: onSelection(e); break;
130             case SWT.KeyDown: onKeyDown(e); break;
131             default:
132             }
133         }
134     };
135     int[] tableEvents = [SWT.MouseDown,
136                                    SWT.Selection,
137                                    SWT.DefaultSelection,
138                                    SWT.KeyDown];
139     for (int i = 0; i < tableEvents.length; i++) {
140         table.addListener(tableEvents[i], tableListener);
141     }
142 
143     Listener listener = new class() Listener {
144         public void handleEvent(Event e) {
145             switch (e.type) {
146             case SWT.Dispose: onDispose(e); break;
147             case SWT.Resize:  onResize(e); break;
148             case SWT.FocusIn: onFocusIn(e); break;
149             default:
150             }
151         }
152     };
153     int[] events = [SWT.Dispose,
154                               SWT.Resize,
155                               SWT.FocusIn];
156     for (int i = 0; i < events.length; i++) {
157         addListener(events[i], listener);
158     }
159 }
160 
161 int addItem(TableTreeItem item, int index) {
162     if (index < 0 || index > items.length) SWT.error(SWT.ERROR_INVALID_ARGUMENT);
163     TableTreeItem[] newItems = new TableTreeItem[items.length + 1];
164     System.arraycopy(items, 0, newItems, 0, index);
165     newItems[index] = item;
166     System.arraycopy(items, index, newItems, index + 1, items.length - index);
167     items = newItems;
168 
169     /* Return the index in the table where this table should be inserted */
170     if (index is items.length - 1 )
171         return table.getItemCount();
172     else
173         return table.indexOf(items[index+1].tableItem);
174 }
175 
176 /**
177  * Adds the listener to the collection of listeners who will
178  * be notified when the user changes the receiver's selection, by sending
179  * it one of the messages defined in the <code>SelectionListener</code>
180  * interface.
181  * <p>
182  * When <code>widgetSelected</code> is called, the item field of the event object is valid.
183  * If the receiver has <code>SWT.CHECK</code> style set and the check selection changes,
184  * the event object detail field contains the value <code>SWT.CHECK</code>.
185  * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
186  * The item field of the event object is valid for default selection, but the detail field is not used.
187  * </p>
188  *
189  * @param listener the listener which should be notified when the user changes the receiver's selection
190  *
191  * @exception IllegalArgumentException <ul>
192  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
193  * </ul>
194  * @exception SWTException <ul>
195  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
196  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
197  * </ul>
198  *
199  * @see SelectionListener
200  * @see #removeSelectionListener
201  * @see SelectionEvent
202  */
203 public void addSelectionListener(SelectionListener listener) {
204     checkWidget();
205     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
206     TypedListener typedListener = new TypedListener (listener);
207     addListener (SWT.Selection,typedListener);
208     addListener (SWT.DefaultSelection,typedListener);
209 }
210 
211 /**
212  * Adds the listener to the collection of listeners who will
213  * be notified when an item in the receiver is expanded or collapsed
214  * by sending it one of the messages defined in the <code>TreeListener</code>
215  * interface.
216  *
217  * @param listener the listener which should be notified
218  *
219  * @exception IllegalArgumentException <ul>
220  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
221  * </ul>
222  * @exception SWTException <ul>
223  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
224  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
225  * </ul>
226  *
227  * @see TreeListener
228  * @see #removeTreeListener
229  */
230 public void addTreeListener(TreeListener listener) {
231     checkWidget();
232     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
233     TypedListener typedListener = new TypedListener (listener);
234     addListener (SWT.Expand, typedListener);
235     addListener (SWT.Collapse, typedListener);
236 }
237 private static int checkStyle (int style) {
238     int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT;
239     style = style & mask;
240     return style;
241 }
242 public override Point computeSize (int wHint, int hHint, bool changed) {
243     checkWidget();
244     return table.computeSize (wHint, hHint, changed);
245 }
246 public override Rectangle computeTrim (int x, int y, int width, int height) {
247     checkWidget();
248     return table.computeTrim(x, y, width, height);
249 }
250 
251 /**
252  * Deselects all items.
253  * <p>
254  * If an item is selected, it is deselected.
255  * If an item is not selected, it remains unselected.
256  *
257  * @exception SWTException <ul>
258  *  <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
259  *  <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
260  * </ul>
261  */
262 public void deselectAll () {
263     checkWidget();
264     table.deselectAll();
265 }
266 
267 /* Expand upward from the specified leaf item. */
268 void expandItem (TableTreeItem item) {
269     if (item is null) return;
270     expandItem(item.parentItem);
271     if (!item.getVisible()) item.setVisible(true);
272     if ( !item.expanded && item.items.length > 0) {
273         item.setExpanded(true);
274         Event event = new Event();
275         event.item = item;
276         notifyListeners(SWT.Expand, event);
277     }
278 }
279 public override Color getBackground () {
280     // This method must be overridden otherwise, in a TableTree in which the first
281     // item has no sub items, a grey (Widget background colour) square will appear in
282     // the first column of the first item.
283     // It is not possible in the constructor to set the background of the TableTree
284     // to be the same as the background of the Table because this interferes with
285     // the TableTree adapting to changes in the System color settings.
286     return table.getBackground();
287 }
288 public override Rectangle getClientArea () {
289     return table.getClientArea();
290 }
291 public override Color getForeground () {
292     return table.getForeground();
293 }
294 public override Font getFont () {
295     return table.getFont();
296 }
297 /**
298  * Gets the number of items.
299  * <p>
300  * @return the number of items in the widget
301  */
302 public int getItemCount () {
303     //checkWidget();
304     return cast(int)/*64bit*/items.length;
305 }
306 
307 /**
308  * Gets the height of one item.
309  * <p>
310  * This operation will fail if the height of
311  * one item could not be queried from the OS.
312  *
313  * @return the height of one item in the widget
314  *
315  * @exception SWTException <ul>
316  *  <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
317  *  <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
318  * </ul>
319  */
320 public int getItemHeight () {
321     checkWidget();
322     return table.getItemHeight();
323 }
324 
325 /**
326  * Gets the items.
327  * <p>
328  * @return the items in the widget
329  */
330 public TableTreeItem [] getItems () {
331     //checkWidget();
332     TableTreeItem[] newItems = new TableTreeItem[items.length];
333     System.arraycopy(items, 0, newItems, 0, items.length);
334     return newItems;
335 }
336 
337 /**
338  * Gets the selected items.
339  * <p>
340  * This operation will fail if the selected
341  * items cannot be queried from the OS.
342  *
343  * @return the selected items in the widget
344  *
345  * @exception SWTException <ul>
346  *      <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
347  *      <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
348  *  </ul>
349  */
350 public TableTreeItem [] getSelection () {
351     checkWidget();
352     TableItem[] selection = table.getSelection();
353     TableTreeItem [] result = new TableTreeItem[selection.length];
354     for (int i = 0; i < selection.length; i++){
355         result[i] = cast(TableTreeItem) selection[i].getData(ITEMID);
356     }
357     return result;
358 }
359 
360 /**
361  * Gets the number of selected items.
362  * <p>
363  * This operation will fail if the number of selected
364  * items cannot be queried from the OS.
365  *
366  * @return the number of selected items in the widget
367  *
368  * @exception SWTException <ul>
369  *      <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread</li>
370  *      <li>ERROR_WIDGET_DISPOSED when the widget has been disposed</li>
371  *  </ul>
372  */
373 public int getSelectionCount () {
374     checkWidget();
375     return table.getSelectionCount();
376 }
377 
378 public override int getStyle () {
379     checkWidget();
380     return table.getStyle();
381 }
382 
383 /**
384  * Returns the underlying Table control.
385  *
386  * @return the underlying Table control
387  */
388 public Table getTable () {
389     //checkWidget();
390     return table;
391 }
392 
393 void createImages () {
394 
395     int itemHeight = sizeImage.getBounds().height;
396     // Calculate border around image.
397     // At least 9 pixels are needed to draw the image
398     // Leave at least a 6 pixel border.
399     int indent = Math.min(6, (itemHeight - 9) / 2);
400     indent = Math.max(0, indent);
401     int size = Math.max (10, itemHeight - 2 * indent);
402     size = ((size + 1) / 2) * 2; // size must be an even number
403     int midpoint = indent + size / 2;
404 
405     Color foreground = getForeground();
406     Color plusMinus = getDisplay().getSystemColor(SWT.COLOR_WIDGET_NORMAL_SHADOW);
407     Color background = getBackground();
408 
409     /* Plus image */
410     PaletteData palette = new PaletteData( [ foreground.getRGB(), background.getRGB(), plusMinus.getRGB()]);
411     ImageData imageData = new ImageData(itemHeight, itemHeight, 4, palette);
412     imageData.transparentPixel = 1;
413     plusImage = new Image(getDisplay(), imageData);
414     GC gc = new GC(plusImage);
415     gc.setBackground(background);
416     gc.fillRectangle(0, 0, itemHeight, itemHeight);
417     gc.setForeground(plusMinus);
418     gc.drawRectangle(indent, indent, size, size);
419     gc.setForeground(foreground);
420     gc.drawLine(midpoint, indent + 2, midpoint, indent + size - 2);
421     gc.drawLine(indent + 2, midpoint, indent + size - 2, midpoint);
422     gc.dispose();
423 
424     /* Minus image */
425     palette = new PaletteData([foreground.getRGB(), background.getRGB(), plusMinus.getRGB()]);
426     imageData = new ImageData(itemHeight, itemHeight, 4, palette);
427     imageData.transparentPixel = 1;
428     minusImage = new Image(getDisplay(), imageData);
429     gc = new GC(minusImage);
430     gc.setBackground(background);
431     gc.fillRectangle(0, 0, itemHeight, itemHeight);
432     gc.setForeground(plusMinus);
433     gc.drawRectangle(indent, indent, size, size);
434     gc.setForeground(foreground);
435     gc.drawLine(indent + 2, midpoint, indent + size - 2, midpoint);
436     gc.dispose();
437 }
438 
439 Image getPlusImage() {
440     if (plusImage is null) createImages();
441     return plusImage;
442 }
443 
444 Image getMinusImage() {
445     if (minusImage is null) createImages();
446     return minusImage;
447 }
448 
449 /**
450  * Gets the index of an item.
451  *
452  * <p>The widget is searched starting at 0 until an
453  * item is found that is equal to the search item.
454  * If no item is found, -1 is returned.  Indexing
455  * is zero based.  This index is relative to the parent only.
456  *
457  * @param item the search item
458  * @return the index of the item or -1
459  */
460 public int indexOf (TableTreeItem item) {
461     //checkWidget();
462     for (int i = 0; i < items.length; i++) {
463         if (item is items[i]) return i;
464     }
465     return -1;
466 }
467 
468 void onDispose(Event e) {
469     /*
470      * Usually when an item is disposed, destroyItem will change the size of the items array
471      * and dispose of the underlying table items.
472      * Since the whole table tree is being disposed, this is not necessary.  For speed
473      * the inDispose flag is used to skip over this part of the item dispose.
474      */
475     inDispose = true;
476     for (int i = 0; i < items.length; i++) {
477         items[i].dispose();
478     }
479     inDispose = false;
480     if (plusImage !is null) plusImage.dispose();
481     if (minusImage !is null) minusImage.dispose();
482     if (sizeImage !is null) sizeImage.dispose();
483     plusImage = minusImage = sizeImage = null;
484 }
485 
486 void onResize(Event e) {
487     Point size = getSize();
488     table.setBounds(0, 0, size.x, size.y);
489 }
490 
491 void onSelection(Event e) {
492     Event event = new Event();
493     TableItem tableItem = cast(TableItem)e.item;
494     TableTreeItem item = getItem(tableItem);
495     event.item = item;
496 
497     if (e.type is SWT.Selection && e.detail is SWT.CHECK && item !is null) {
498         event.detail = SWT.CHECK;
499         item.checked = tableItem.getChecked();
500     }
501     notifyListeners(e.type, event);
502 }
503 /**
504  * Returns the item at the given, zero-relative index in the
505  * receiver. Throws an exception if the index is out of range.
506  *
507  * @param index the index of the item to return
508  * @return the item at the given index
509  *
510  * @exception IllegalArgumentException <ul>
511  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
512  * </ul>
513  * @exception SWTException <ul>
514  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
515  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
516  * </ul>
517  *
518  * @since 3.1
519  */
520 public TableTreeItem getItem (int index) {
521     checkWidget();
522     int count = cast(int)/*64bit*/items.length;
523     if (!(0 <= index && index < count)) SWT.error (SWT.ERROR_INVALID_RANGE);
524     return items [index];
525 }
526 
527 /**
528  * Returns the item at the given point in the receiver
529  * or null if no such item exists. The point is in the
530  * coordinate system of the receiver.
531  *
532  * @param point the point used to locate the item
533  * @return the item at the given point
534  *
535  * @exception IllegalArgumentException <ul>
536  *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
537  * </ul>
538  * @exception SWTException <ul>
539  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
540  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
541  * </ul>
542  */
543 public TableTreeItem getItem(Point point) {
544     checkWidget();
545     TableItem item = table.getItem(point);
546     if (item is null) return null;
547     return getItem(item);
548 
549 }
550 TableTreeItem getItem(TableItem tableItem) {
551     if (tableItem is null) return null;
552     for (int i = 0; i < items.length; i++) {
553             TableTreeItem item = items[i].getItem(tableItem);
554             if (item !is null) return item;
555     }
556     return null;
557 }
558 void onFocusIn (Event e) {
559     table.setFocus();
560 }
561 
562 void onKeyDown (Event e) {
563     TableTreeItem[] selection = getSelection();
564     if (selection.length is 0) return;
565     TableTreeItem item = selection[0];
566     int type = 0;
567     if (e.keyCode is SWT.ARROW_RIGHT || e.keyCode is SWT.ARROW_LEFT) {
568         int trailKey = (getStyle() & SWT.MIRRORED) !is 0 ? SWT.ARROW_LEFT : SWT.ARROW_RIGHT;
569         if (e.keyCode is trailKey) {
570             if (item.getItemCount() is 0) return;
571             if (item.getExpanded()) {
572                 TableTreeItem newSelection = item.getItems()[0];
573                 table.setSelection([newSelection.tableItem]);
574                 showItem(newSelection);
575                 type = SWT.Selection;
576             } else {
577                 item.setExpanded(true);
578                 type = SWT.Expand;
579             }
580         } else {
581             if (item.getExpanded()) {
582                 item.setExpanded(false);
583                 type = SWT.Collapse;
584             } else {
585                 TableTreeItem parent = item.getParentItem();
586                 if (parent !is null) {
587                     int index = parent.indexOf(item);
588                     if (index !is 0) return;
589                     table.setSelection([parent.tableItem]);
590                     type = SWT.Selection;
591                 }
592             }
593         }
594     }
595     if (e.character is '*') {
596         item.expandAll(true);
597     }
598     if (e.character is '-') {
599         if (item.getExpanded()) {
600             item.setExpanded(false);
601             type = SWT.Collapse;
602         }
603     }
604     if (e.character is '+') {
605         if (item.getItemCount() > 0 && !item.getExpanded()) {
606             item.setExpanded(true);
607             type = SWT.Expand;
608         }
609     }
610     if (type is 0) return;
611     Event event = new Event();
612     event.item = item;
613     notifyListeners(type, event);
614 }
615 void onMouseDown(Event event) {
616     /* If user clicked on the [+] or [-], expand or collapse the tree. */
617     TableItem[] items = table.getItems();
618     for (int i = 0; i < items.length; i++) {
619         Rectangle rect = items[i].getImageBounds(0);
620         if (rect.contains(event.x, event.y)) {
621             TableTreeItem item = cast(TableTreeItem) items[i].getData(ITEMID);
622             event = new Event();
623             event.item = item;
624             item.setExpanded(!item.getExpanded());
625             if (item.getExpanded()) {
626                 notifyListeners(SWT.Expand, event);
627             } else {
628                 notifyListeners(SWT.Collapse, event);
629             }
630             return;
631         }
632     }
633 }
634 
635 /**
636  * Removes all items.
637  * <p>
638  * This operation will fail when an item
639  * could not be removed in the OS.
640  *
641  * @exception SWTException <ul>
642  *  <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
643  *  <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
644  * </ul>
645  */
646 public void removeAll () {
647     checkWidget();
648     setRedraw(false);
649     for (ptrdiff_t i = cast(ptrdiff_t) (items.length) - 1; i >= 0; i--) {
650         items[i].dispose();
651     }
652     items = EMPTY_ITEMS;
653     setRedraw(true);
654 }
655 
656 void removeItem(TableTreeItem item) {
657     int index = 0;
658     while (index < items.length && items[index] !is item) index++;
659     if (index is items.length) return;
660     TableTreeItem[] newItems = new TableTreeItem[items.length - 1];
661     System.arraycopy(items, 0, newItems, 0, index);
662     System.arraycopy(items, index + 1, newItems, index, items.length - index - 1);
663     items = newItems;
664 }
665 
666 /**
667  * Removes the listener from the collection of listeners who will
668  * be notified when the user changes the receiver's selection.
669  *
670  * @param listener the listener which should no longer be notified
671  *
672  * @exception IllegalArgumentException <ul>
673  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
674  * </ul>
675  * @exception SWTException <ul>
676  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
677  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
678  * </ul>
679  *
680  * @see SelectionListener
681  * @see #addSelectionListener
682  */
683 public void removeSelectionListener (SelectionListener listener) {
684     checkWidget();
685     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
686     removeListener(SWT.Selection, listener);
687     removeListener(SWT.DefaultSelection, listener);
688 }
689 
690 /**
691  * Removes the listener from the collection of listeners who will
692  * be notified when items in the receiver are expanded or collapsed.
693  *
694  * @param listener the listener which should no longer be notified
695  *
696  * @exception IllegalArgumentException <ul>
697  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
698  * </ul>
699  * @exception SWTException <ul>
700  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
701  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
702  * </ul>
703  *
704  * @see TreeListener
705  * @see #addTreeListener
706  */
707 public void removeTreeListener (TreeListener listener) {
708     checkWidget();
709     if (listener is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
710     removeListener(SWT.Expand, listener);
711     removeListener(SWT.Collapse, listener);
712 }
713 
714 /**
715  * Selects all of the items in the receiver.
716  * <p>
717  * If the receiver is single-select, do nothing.
718  *
719  * @exception SWTException <ul>
720  *  <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
721  *  <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
722  * </ul>
723  */
724 public void selectAll () {
725     checkWidget();
726     table.selectAll();
727 }
728 public override void setBackground (Color color) {
729     super.setBackground(color);
730     table.setBackground(color);
731     if (sizeImage !is null) {
732         GC gc = new GC (sizeImage);
733         gc.setBackground(getBackground());
734         Rectangle size = sizeImage.getBounds();
735         gc.fillRectangle(size);
736         gc.dispose();
737     }
738 }
739 public override void setEnabled (bool enabled) {
740     super.setEnabled(enabled);
741     table.setEnabled(enabled);
742 }
743 public override void setFont (Font font) {
744     super.setFont(font);
745     table.setFont(font);
746 }
747 public override void setForeground (Color color) {
748     super.setForeground(color);
749     table.setForeground(color);
750 }
751 public override void setMenu (Menu menu) {
752     super.setMenu(menu);
753     table.setMenu(menu);
754 }
755 
756 /**
757  * Sets the receiver's selection to be the given array of items.
758  * The current selection is cleared before the new items are selected.
759  * <p>
760  * Items that are not in the receiver are ignored.
761  * If the receiver is single-select and multiple items are specified,
762  * then all items are ignored.
763  *
764  * @param items the array of items
765  *
766  * @exception IllegalArgumentException <ul>
767  *    <li>ERROR_INVALID_ARGUMENT - if one of the item has been disposed</li>
768  * </ul>
769  * @exception SWTException <ul>
770  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
771  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
772  * </ul>
773  *
774  * @see TableTree#deselectAll()
775  */
776 public void setSelection (TableTreeItem[] items) {
777     checkWidget ();
778     // SWT extension: allow null for zero length string
779     //if (items is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
780     int length = cast(int)/*64bit*/items.length;
781     if (length is 0 || ((table.getStyle() & SWT.SINGLE) !is 0 && length > 1)) {
782         deselectAll();
783         return;
784     }
785     TableItem[] tableItems = new TableItem[length];
786     for (int i = 0; i < length; i++) {
787         if (items[i] is null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
788         if (!items[i].getVisible()) expandItem (items[i]);
789         tableItems[i] = items[i].tableItem;
790     }
791     table.setSelection(tableItems);
792 }
793 public override void setToolTipText (String string) {
794     super.setToolTipText(string);
795     table.setToolTipText(string);
796 }
797 
798 /**
799  * Shows the item.  If the item is already showing in the receiver,
800  * this method simply returns.  Otherwise, the items are scrolled
801  * and expanded until the item is visible.
802  *
803  * @param item the item to be shown
804  *
805  * @exception IllegalArgumentException <ul>
806  *    <li>ERROR_NULL_ARGUMENT - if the item is null</li>
807  *    <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li>
808  * </ul>
809  * @exception SWTException <ul>
810  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
811  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
812  * </ul>
813  *
814  * @see TableTree#showSelection()
815  */
816 public void showItem (TableTreeItem item) {
817     checkWidget();
818     if (item is null) SWT.error (SWT.ERROR_NULL_ARGUMENT);
819     if (!item.getVisible()) expandItem (item);
820     TableItem tableItem = item.tableItem;
821     table.showItem(tableItem);
822 }
823 
824 /**
825  * Shows the selection.
826  * <p>
827  * If there is no selection or the selection
828  * is already visible, this method does nothing.
829  * If the selection is scrolled out of view,
830  * the top index of the widget is changed such
831  * that selection becomes visible.
832  *
833  * @exception SWTException <ul>
834  *  <li>ERROR_THREAD_INVALID_ACCESS when called from the wrong thread
835  *  <li>ERROR_WIDGET_DISPOSED when the widget has been disposed
836  * </ul>
837  */
838 public void showSelection () {
839     checkWidget();
840     table.showSelection();
841 }
842 }