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.TableCursor;
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.SelectionEvent;
21 import org.eclipse.swt.events.SelectionListener;
22 import org.eclipse.swt.graphics.Color;
23 import org.eclipse.swt.graphics.GC;
24 import org.eclipse.swt.graphics.Image;
25 import org.eclipse.swt.graphics.Point;
26 import org.eclipse.swt.graphics.Rectangle;
27 import org.eclipse.swt.widgets.Canvas;
28 import org.eclipse.swt.widgets.Display;
29 import org.eclipse.swt.widgets.Event;
30 import org.eclipse.swt.widgets.Listener;
31 import org.eclipse.swt.widgets.ScrollBar;
32 import org.eclipse.swt.widgets.Table;
33 import org.eclipse.swt.widgets.TableColumn;
34 import org.eclipse.swt.widgets.TableItem;
35 import org.eclipse.swt.widgets.TypedListener;
36 import org.eclipse.swt.widgets.Widget;
37 
38 /**
39  * A TableCursor provides a way for the user to navigate around a Table
40  * using the keyboard.  It also provides a mechanism for selecting an
41  * individual cell in a table.
42  *
43  * <p> Here is an example of using a TableCursor to navigate to a cell and then edit it.
44  *
45  * <code><pre>
46  *  public static void main(String[] args) {
47  *      Display display = new Display();
48  *      Shell shell = new Shell(display);
49  *      shell.setLayout(new GridLayout());
50  *
51  *      // create a a table with 3 columns and fill with data
52  *      final Table table = new Table(shell, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION);
53  *      table.setLayoutData(new GridData(GridData.FILL_BOTH));
54  *      TableColumn column1 = new TableColumn(table, SWT.NONE);
55  *      TableColumn column2 = new TableColumn(table, SWT.NONE);
56  *      TableColumn column3 = new TableColumn(table, SWT.NONE);
57  *      for (int i = 0; i &lt; 100; i++) {
58  *          TableItem item = new TableItem(table, SWT.NONE);
59  *          item.setText(new String[] { "cell "+i+" 0", "cell "+i+" 1", "cell "+i+" 2"});
60  *      }
61  *      column1.pack();
62  *      column2.pack();
63  *      column3.pack();
64  *
65  *      // create a TableCursor to navigate around the table
66  *      final TableCursor cursor = new TableCursor(table, SWT.NONE);
67  *      // create an editor to edit the cell when the user hits "ENTER"
68  *      // while over a cell in the table
69  *      final ControlEditor editor = new ControlEditor(cursor);
70  *      editor.grabHorizontal = true;
71  *      editor.grabVertical = true;
72  *
73  *      cursor.addSelectionListener(new SelectionAdapter() {
74  *          // when the TableEditor is over a cell, select the corresponding row in
75  *          // the table
76  *          public void widgetSelected(SelectionEvent e) {
77  *              table.setSelection(new TableItem[] {cursor.getRow()});
78  *          }
79  *          // when the user hits "ENTER" in the TableCursor, pop up a text editor so that
80  *          // they can change the text of the cell
81  *          public void widgetDefaultSelected(SelectionEvent e){
82  *              final Text text = new Text(cursor, SWT.NONE);
83  *              TableItem row = cursor.getRow();
84  *              int column = cursor.getColumn();
85  *              text.setText(row.getText(column));
86  *              text.addKeyListener(new KeyAdapter() {
87  *                  public void keyPressed(KeyEvent e) {
88  *                      // close the text editor and copy the data over
89  *                      // when the user hits "ENTER"
90  *                      if (e.character is SWT.CR) {
91  *                          TableItem row = cursor.getRow();
92  *                          int column = cursor.getColumn();
93  *                          row.setText(column, text.getText());
94  *                          text.dispose();
95  *                      }
96  *                      // close the text editor when the user hits "ESC"
97  *                      if (e.character is SWT.ESC) {
98  *                          text.dispose();
99  *                      }
100  *                  }
101  *              });
102  *              editor.setEditor(text);
103  *              text.setFocus();
104  *          }
105  *      });
106  *      // Hide the TableCursor when the user hits the "MOD1" or "MOD2" key.
107  *      // This allows the user to select multiple items in the table.
108  *      cursor.addKeyListener(new KeyAdapter() {
109  *          public void keyPressed(KeyEvent e) {
110  *              if (e.keyCode is SWT.MOD1 ||
111  *                  e.keyCode is SWT.MOD2 ||
112  *                  (e.stateMask & SWT.MOD1) !is 0 ||
113  *                  (e.stateMask & SWT.MOD2) !is 0) {
114  *                  cursor.setVisible(false);
115  *              }
116  *          }
117  *      });
118  *      // Show the TableCursor when the user releases the "MOD2" or "MOD1" key.
119  *      // This signals the end of the multiple selection task.
120  *      table.addKeyListener(new KeyAdapter() {
121  *          public void keyReleased(KeyEvent e) {
122  *              if (e.keyCode is SWT.MOD1 && (e.stateMask & SWT.MOD2) !is 0) return;
123  *              if (e.keyCode is SWT.MOD2 && (e.stateMask & SWT.MOD1) !is 0) return;
124  *              if (e.keyCode !is SWT.MOD1 && (e.stateMask & SWT.MOD1) !is 0) return;
125  *              if (e.keyCode !is SWT.MOD2 && (e.stateMask & SWT.MOD2) !is 0) return;
126  *
127  *              TableItem[] selection = table.getSelection();
128  *              TableItem row = (selection.length is 0) ? table.getItem(table.getTopIndex()) : selection[0];
129  *              table.showItem(row);
130  *              cursor.setSelection(row, 0);
131  *              cursor.setVisible(true);
132  *              cursor.setFocus();
133  *          }
134  *      });
135  *
136  *      shell.open();
137  *      while (!shell.isDisposed()) {
138  *          if (!display.readAndDispatch())
139  *              display.sleep();
140  *      }
141  *      display.dispose();
142  *  }
143  * </pre></code>
144  *
145  * <dl>
146  * <dt><b>Styles:</b></dt>
147  * <dd>BORDER</dd>
148  * <dt><b>Events:</b></dt>
149  * <dd>Selection, DefaultSelection</dd>
150  * </dl>
151  *
152  * @since 2.0
153  *
154  * @see <a href="http://www.eclipse.org/swt/snippets/#tablecursor">TableCursor snippets</a>
155  * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 
156  */
157 public class TableCursor : Canvas {
158 
159     alias Canvas.dispose dispose;
160 
161     Table table;
162     TableItem row = null;
163     TableColumn column = null;
164     Listener tableListener, resizeListener, disposeItemListener, disposeColumnListener;
165 
166     Color background = null;
167     Color foreground = null;
168 
169     // By default, invert the list selection colors
170     static const int BACKGROUND = SWT.COLOR_LIST_SELECTION_TEXT;
171     static const int FOREGROUND = SWT.COLOR_LIST_SELECTION;
172 
173 /**
174  * Constructs a new instance of this class given its parent
175  * table and a style value describing its behavior and appearance.
176  * <p>
177  * The style value is either one of the style constants defined in
178  * class <code>SWT</code> which is applicable to instances of this
179  * class, or must be built by <em>bitwise OR</em>'ing together
180  * (that is, using the <code>int</code> "|" operator) two or more
181  * of those <code>SWT</code> style constants. The class description
182  * lists the style constants that are applicable to the class.
183  * Style bits are also inherited from superclasses.
184  * </p>
185  *
186  * @param parent a Table control which will be the parent of the new instance (cannot be null)
187  * @param style the style of control to construct
188  *
189  * @exception IllegalArgumentException <ul>
190  *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
191  * </ul>
192  * @exception SWTException <ul>
193  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
194  *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
195  * </ul>
196  *
197  * @see SWT#BORDER
198  * @see Widget#checkSubclass()
199  * @see Widget#getStyle()
200  */
201 public this(Table parent, int style) {
202     super(parent, style);
203     table = parent;
204     setBackground(null);
205     setForeground(null);
206 
207     Listener listener = new class() Listener {
208         public void handleEvent(Event event) {
209             switch (event.type) {
210                 case SWT.Dispose :
211                     TableCursor.dispose(event);
212                     break;
213                 case SWT.FocusIn :
214                 case SWT.FocusOut :
215                     redraw();
216                     break;
217                 case SWT.KeyDown :
218                     keyDown(event);
219                     break;
220                 case SWT.Paint :
221                     paint(event);
222                     break;
223                 case SWT.Traverse : {
224                     event.doit = true;
225                     switch (event.detail) {
226                         case SWT.TRAVERSE_ARROW_NEXT :
227                         case SWT.TRAVERSE_ARROW_PREVIOUS :
228                         case SWT.TRAVERSE_RETURN :
229                             event.doit = false;
230                             break;
231                         default:
232                     }
233                     break;
234                 }
235                 default:
236             }
237         }
238     };
239     int[] events = [SWT.Dispose, SWT.FocusIn, SWT.FocusOut, SWT.KeyDown, SWT.Paint, SWT.Traverse];
240     for (int i = 0; i < events.length; i++) {
241         addListener(events[i], listener);
242     }
243 
244     tableListener = new class() Listener {
245         public void handleEvent(Event event) {
246             switch (event.type) {
247                 case SWT.MouseDown :
248                     tableMouseDown(event);
249                     break;
250                 case SWT.FocusIn :
251                     tableFocusIn(event);
252                     break;
253                 default:
254             }
255         }
256     };
257     table.addListener(SWT.FocusIn, tableListener);
258     table.addListener(SWT.MouseDown, tableListener);
259 
260     disposeItemListener = new class() Listener {
261         public void handleEvent(Event event) {
262             unhookRowColumnListeners();
263             row = null;
264             column = null;
265             _resize();
266         }
267     };
268     disposeColumnListener = new class() Listener {
269         public void handleEvent(Event event) {
270             unhookRowColumnListeners();
271             row = null;
272             column = null;
273             _resize();
274         }
275     };
276     resizeListener = new class() Listener {
277         public void handleEvent(Event event) {
278             _resize();
279         }
280     };
281     ScrollBar hBar = table.getHorizontalBar();
282     if (hBar !is null) {
283         hBar.addListener(SWT.Selection, resizeListener);
284     }
285     ScrollBar vBar = table.getVerticalBar();
286     if (vBar !is null) {
287         vBar.addListener(SWT.Selection, resizeListener);
288     }
289 }
290 
291 /**
292  * Adds the listener to the collection of listeners who will
293  * be notified when the user changes the receiver's selection, by sending
294  * it one of the messages defined in the <code>SelectionListener</code>
295  * interface.
296  * <p>
297  * When <code>widgetSelected</code> is called, the item field of the event object is valid.
298  * If the receiver has <code>SWT.CHECK</code> style set and the check selection changes,
299  * the event object detail field contains the value <code>SWT.CHECK</code>.
300  * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
301  * </p>
302  *
303  * @param listener the listener which should be notified when the user changes the receiver's selection
304  *
305  * @exception IllegalArgumentException <ul>
306  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
307  * </ul>
308  * @exception SWTException <ul>
309  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
310  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
311  * </ul>
312  *
313  * @see SelectionListener
314  * @see SelectionEvent
315  * @see #removeSelectionListener(SelectionListener)
316  *
317  */
318 public void addSelectionListener(SelectionListener listener) {
319     checkWidget();
320     if (listener is null)
321         SWT.error(SWT.ERROR_NULL_ARGUMENT);
322     TypedListener typedListener = new TypedListener(listener);
323     addListener(SWT.Selection, typedListener);
324     addListener(SWT.DefaultSelection, typedListener);
325 }
326 
327 void dispose(Event event) {
328     table.removeListener(SWT.FocusIn, tableListener);
329     table.removeListener(SWT.MouseDown, tableListener);
330     unhookRowColumnListeners();
331     ScrollBar hBar = table.getHorizontalBar();
332     if (hBar !is null) {
333         hBar.removeListener(SWT.Selection, resizeListener);
334     }
335     ScrollBar vBar = table.getVerticalBar();
336     if (vBar !is null) {
337         vBar.removeListener(SWT.Selection, resizeListener);
338     }
339 }
340 
341 void keyDown(Event event) {
342     if (row is null) return;
343     switch (event.character) {
344         case SWT.CR :
345             notifyListeners(SWT.DefaultSelection, new Event());
346             return;
347         default:
348     }
349     int rowIndex = table.indexOf(row);
350     int columnIndex = column is null ? 0 : table.indexOf(column);
351     switch (event.keyCode) {
352         case SWT.ARROW_UP :
353             setRowColumn(Math.max(0, rowIndex - 1), columnIndex, true);
354             break;
355         case SWT.ARROW_DOWN :
356             setRowColumn(Math.min(rowIndex + 1, table.getItemCount() - 1), columnIndex, true);
357             break;
358         case SWT.ARROW_LEFT :
359         case SWT.ARROW_RIGHT :
360             {
361                 int columnCount = table.getColumnCount();
362                 if (columnCount is 0) break;
363                 int[] order = table.getColumnOrder();
364                 int index = 0;
365                 while (index < order.length) {
366                     if (order[index] is columnIndex) break;
367                     index++;
368                 }
369                 if (index is order.length) index = 0;
370                 int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) !is 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
371                 if (event.keyCode is leadKey) {
372                    setRowColumn(rowIndex, order[Math.max(0, index - 1)], true);
373                 } else {
374                    setRowColumn(rowIndex, order[Math.min(columnCount - 1, index + 1)], true);
375                 }
376                 break;
377             }
378         case SWT.HOME :
379             setRowColumn(0, columnIndex, true);
380             break;
381         case SWT.END :
382             {
383                 int i = table.getItemCount() - 1;
384                 setRowColumn(i, columnIndex, true);
385                 break;
386             }
387         case SWT.PAGE_UP :
388             {
389                 int index = table.getTopIndex();
390                 if (index is rowIndex) {
391                     Rectangle rect = table.getClientArea();
392                     TableItem item = table.getItem(index);
393                     Rectangle itemRect = item.getBounds(0);
394                     rect.height -= itemRect.y;
395                     int height = table.getItemHeight();
396                     int page = Math.max(1, rect.height / height);
397                     index = Math.max(0, index - page + 1);
398                 }
399                 setRowColumn(index, columnIndex, true);
400                 break;
401             }
402         case SWT.PAGE_DOWN :
403             {
404                 int index = table.getTopIndex();
405                 Rectangle rect = table.getClientArea();
406                 TableItem item = table.getItem(index);
407                 Rectangle itemRect = item.getBounds(0);
408                 rect.height -= itemRect.y;
409                 int height = table.getItemHeight();
410                 int page = Math.max(1, rect.height / height);
411                 int end = table.getItemCount() - 1;
412                 index = Math.min(end, index + page - 1);
413                 if (index is rowIndex) {
414                     index = Math.min(end, index + page - 1);
415                 }
416                 setRowColumn(index, columnIndex, true);
417                 break;
418             }
419         default:
420     }
421 }
422 
423 void paint(Event event) {
424     if (row is null) return;
425     int columnIndex = column is null ? 0 : table.indexOf(column);
426     GC gc = event.gc;
427     Display display = getDisplay();
428     gc.setBackground(getBackground());
429     gc.setForeground(getForeground());
430     gc.fillRectangle(event.x, event.y, event.width, event.height);
431     int x = 0;
432     Point size = getSize();
433     Image image = row.getImage(columnIndex);
434     if (image !is null) {
435         Rectangle imageSize = image.getBounds();
436         int imageY = (size.y - imageSize.height) / 2;
437         gc.drawImage(image, x, imageY);
438         x += imageSize.width;
439     }
440     String text = row.getText(columnIndex);
441     if (text.length > 0) {
442         Rectangle bounds = row.getBounds(columnIndex);
443         Point extent = gc.stringExtent(text);
444         // Temporary code - need a better way to determine table trim
445         String platform = SWT.getPlatform();
446         if ("win32"==platform) { //$NON-NLS-1$
447             if (table.getColumnCount() is 0 || columnIndex is 0) {
448                 x += 2;
449             } else {
450                 int alignmnent = column.getAlignment();
451                 switch (alignmnent) {
452                     case SWT.LEFT:
453                         x += 6;
454                         break;
455                     case SWT.RIGHT:
456                         x = bounds.width - extent.x - 6;
457                         break;
458                     case SWT.CENTER:
459                         x += (bounds.width - x - extent.x) / 2;
460                         break;
461                     default:
462                 }
463             }
464         }  else {
465             if (table.getColumnCount() is 0) {
466                 x += 5;
467             } else {
468                 int alignmnent = column.getAlignment();
469                 switch (alignmnent) {
470                     case SWT.LEFT:
471                         x += 5;
472                         break;
473                     case SWT.RIGHT:
474                         x = bounds.width- extent.x - 2;
475                         break;
476                     case SWT.CENTER:
477                         x += (bounds.width - x - extent.x) / 2 + 2;
478                         break;
479                     default:
480                 }
481             }
482         }
483         int textY = (size.y - extent.y) / 2;
484         gc.drawString(text, x, textY);
485     }
486     if (isFocusControl()) {
487         gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
488         gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
489         gc.drawFocus(0, 0, size.x, size.y);
490     }
491 }
492 
493 void tableFocusIn(Event event) {
494     if (isDisposed()) return;
495     if (isVisible()) {
496         if (row is null && column is null) return;
497         setFocus();
498     }
499 }
500 
501 void tableMouseDown(Event event) {
502     if (isDisposed() || !isVisible()) return;
503     Point pt = new Point(event.x, event.y);
504     int lineWidth = table.getLinesVisible() ? table.getGridLineWidth() : 0;
505     TableItem item = table.getItem(pt);
506     if ((table.getStyle() & SWT.FULL_SELECTION) !is 0) {
507         if (item is null) return;
508     } else {
509         int start = item !is null ? table.indexOf(item) : table.getTopIndex();
510         int end = table.getItemCount();
511         Rectangle clientRect = table.getClientArea();
512         for (int i = start; i < end; i++) {
513             TableItem nextItem = table.getItem(i);
514             Rectangle rect = nextItem.getBounds(0);
515             if (pt.y >= rect.y && pt.y < rect.y + rect.height + lineWidth) {
516                 item = nextItem;
517                 break;
518             }
519             if (rect.y > clientRect.y + clientRect.height)  return;
520         }
521         if (item is null) return;
522     }
523     TableColumn newColumn = null;
524     int columnCount = table.getColumnCount();
525     if (columnCount is 0) {
526         if ((table.getStyle() & SWT.FULL_SELECTION) is 0) {
527             Rectangle rect = item.getBounds(0);
528             rect.width += lineWidth;
529             rect.height += lineWidth;
530             if (!rect.contains(pt)) return;
531         }
532     } else {
533         for (int i = 0; i < columnCount; i++) {
534             Rectangle rect = item.getBounds(i);
535             rect.width += lineWidth;
536             rect.height += lineWidth;
537             if (rect.contains(pt)) {
538                 newColumn = table.getColumn(i);
539                 break;
540             }
541         }
542         if (newColumn is null) {
543             if ((table.getStyle() & SWT.FULL_SELECTION) is 0) return;
544             newColumn = table.getColumn(0);
545         }
546     }
547     setRowColumn(item, newColumn, true);
548     setFocus();
549     return;
550 }
551 void setRowColumn(int row, int column, bool notify) {
552     TableItem item = row is -1 ? null : table.getItem(row);
553     TableColumn col = column is -1 || table.getColumnCount() is 0 ? null : table.getColumn(column);
554     setRowColumn(item, col, notify);
555 }
556 void setRowColumn(TableItem row, TableColumn column, bool notify) {
557     if (this.row is row && this.column is column) {
558         return;
559     }
560     if (this.row !is null && this.row !is row) {
561         this.row.removeListener(SWT.Dispose, disposeItemListener);
562         this.row = null;
563     }
564     if (this.column !is null && this.column !is column) {
565         this.column.removeListener(SWT.Dispose, disposeColumnListener);
566         this.column.removeListener(SWT.Move, resizeListener);
567         this.column.removeListener(SWT.Resize, resizeListener);
568         this.column = null;
569     }
570     if (row !is null) {
571         if (this.row !is row) {
572             this.row = row;
573             row.addListener(SWT.Dispose, disposeItemListener);
574             table.showItem(row);
575         }
576         if (this.column !is column && column !is null) {
577             this.column = column;
578             column.addListener(SWT.Dispose, disposeColumnListener);
579             column.addListener(SWT.Move, resizeListener);
580             column.addListener(SWT.Resize, resizeListener);
581             table.showColumn(column);
582         }
583         int columnIndex = column is null ? 0 : table.indexOf(column);
584         setBounds(row.getBounds(columnIndex));
585         redraw();
586         if (notify) {
587             notifyListeners(SWT.Selection, new Event());
588         }
589     }
590 }
591 
592 public override void setVisible(bool visible) {
593     checkWidget();
594     if (visible) _resize();
595     super.setVisible(visible);
596 }
597 
598 /**
599  * Removes the listener from the collection of listeners who will
600  * be notified when the user changes the receiver's selection.
601  *
602  * @param listener the listener which should no longer be notified
603  *
604  * @exception IllegalArgumentException <ul>
605  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
606  * </ul>
607  * @exception SWTException <ul>
608  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
609  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
610  * </ul>
611  *
612  * @see SelectionListener
613  * @see #addSelectionListener(SelectionListener)
614  *
615  * @since 3.0
616  */
617 public void removeSelectionListener(SelectionListener listener) {
618     checkWidget();
619     if (listener is null) {
620         SWT.error(SWT.ERROR_NULL_ARGUMENT);
621     }
622     removeListener(SWT.Selection, listener);
623     removeListener(SWT.DefaultSelection, listener);
624 }
625 
626 void _resize() {
627     if (row is null) {
628         setBounds(-200, -200, 0, 0);
629     } else {
630         int columnIndex = column is null ? 0 : table.indexOf(column);
631         setBounds(row.getBounds(columnIndex));
632     }
633 }
634 /**
635  * Returns the column over which the TableCursor is positioned.
636  *
637  * @return the column for the current position
638  *
639  * @exception SWTException <ul>
640  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
641  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
642  * </ul>
643  */
644 public int getColumn() {
645     checkWidget();
646     return column is null ? 0 : table.indexOf(column);
647 }
648 /**
649  * Returns the background color that the receiver will use to draw.
650  *
651  * @return the receiver's background color
652  */
653 override
654 public Color getBackground() {
655     checkWidget();
656     if (background is null) {
657         return getDisplay().getSystemColor(BACKGROUND);
658     }
659     return background;
660 }
661 /**
662  * Returns the foreground color that the receiver will use to draw.
663  *
664  * @return the receiver's foreground color
665  */
666 override
667 public Color getForeground() {
668     checkWidget();
669     if (foreground is null) {
670         return getDisplay().getSystemColor(FOREGROUND);
671     }
672     return foreground;
673 }
674 /**
675  * Returns the row over which the TableCursor is positioned.
676  *
677  * @return the item for the current position
678  *
679  * @exception SWTException <ul>
680  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
681  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
682  * </ul>
683  */
684 public TableItem getRow() {
685     checkWidget();
686     return row;
687 }
688 /**
689  * Sets the receiver's background color to the color specified
690  * by the argument, or to the default system color for the control
691  * if the argument is null.
692  * <p>
693  * Note: This operation is a hint and may be overridden by the platform.
694  * For example, on Windows the background of a Button cannot be changed.
695  * </p>
696  * @param color the new color (or null)
697  *
698  * @exception IllegalArgumentException <ul>
699  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> 
700  * </ul>
701  * @exception SWTException <ul>
702  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
703  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
704  * </ul>
705  */
706 public override void setBackground (Color color) {
707     background = color;
708     super.setBackground(getBackground());
709     redraw();
710 }
711 /**
712  * Sets the receiver's foreground color to the color specified
713  * by the argument, or to the default system color for the control
714  * if the argument is null.
715  * <p>
716  * Note: This operation is a hint and may be overridden by the platform.
717  * </p>
718  * @param color the new color (or null)
719  *
720  * @exception IllegalArgumentException <ul>
721  *    <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> 
722  * </ul>
723  * @exception SWTException <ul>
724  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
725  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
726  * </ul>
727  */
728 public override void setForeground (Color color) {
729     foreground = color;
730     super.setForeground(getForeground());
731     redraw();
732 }
733 /**
734  * Positions the TableCursor over the cell at the given row and column in the parent table.
735  *
736  * @param row the index of the row for the cell to select
737  * @param column the index of column for the cell to select
738  *
739  * @exception SWTException <ul>
740  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
741  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
742  * </ul>
743  *
744  */
745 public void setSelection(int row, int column) {
746     checkWidget();
747     int columnCount = table.getColumnCount();
748     int maxColumnIndex =  columnCount is 0 ? 0 : columnCount - 1;
749     if (row < 0
750         || row >= table.getItemCount()
751         || column < 0
752         || column > maxColumnIndex)
753         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
754     setRowColumn(row, column, false);
755 }
756 /**
757  * Positions the TableCursor over the cell at the given row and column in the parent table.
758  *
759  * @param row the TableItem of the row for the cell to select
760  * @param column the index of column for the cell to select
761  *
762  * @exception SWTException <ul>
763  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
764  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
765  * </ul>
766  *
767  */
768 public void setSelection(TableItem row, int column) {
769     checkWidget();
770     int columnCount = table.getColumnCount();
771     int maxColumnIndex =  columnCount is 0 ? 0 : columnCount - 1;
772     if (row is null
773         || row.isDisposed()
774         || column < 0
775         || column > maxColumnIndex)
776         SWT.error(SWT.ERROR_INVALID_ARGUMENT);
777     setRowColumn(table.indexOf(row), column, false);
778 }
779 void unhookRowColumnListeners() {
780     if (column !is null) {
781         column.removeListener(SWT.Dispose, disposeColumnListener);
782         column.removeListener(SWT.Move, resizeListener);
783         column.removeListener(SWT.Resize, resizeListener);
784         column = null;
785     }
786     if (row !is null) {
787         row.removeListener(SWT.Dispose, disposeItemListener);
788         row = null;
789     }
790 }
791 }