Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/swt/widgets/Table.java


1   /*******************************************************************************
2    * Copyright (c) 2000, 2004 IBM Corporation and others.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Common Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/cpl-v10.html
7    * 
8    * Contributors:
9    *     IBM Corporation - initial API and implementation
10   *******************************************************************************/
11  package org.eclipse.swt.widgets;
12  
13  
14  import org.eclipse.swt.*;
15  import org.eclipse.swt.internal.*;
16  import org.eclipse.swt.internal.gtk.*;
17  import org.eclipse.swt.graphics.*;
18  import org.eclipse.swt.events.*;
19  
20  /** 
21   * Instances of this class implement a selectable user interface
22   * object that displays a list of images and strings and issue
23   * notificiation when selected.
24   * <p>
25   * The item children that may be added to instances of this class
26   * must be of type <code>TableItem</code>.
27   * </p><p>
28   * Note that although this class is a subclass of <code>Composite</code>,
29   * it does not make sense to add <code>Control</code> children to it,
30   * or set a layout on it.
31   * </p><p>
32   * <dl>
33   * <dt><b>Styles:</b></dt>
34   * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL</dd>
35   * <dt><b>Events:</b></dt>
36   * <dd>Selection, DefaultSelection</dd>
37   * </dl>
38   * <p>
39   * Note: Only one of the styles SINGLE, and MULTI may be specified.
40   * </p><p>
41   * IMPORTANT: This class is <em>not</em> intended to be subclassed.
42   * </p>
43   */
44  public class Table extends Composite {
45    long /*int*/ modelHandle, checkRenderer;
46    int itemCount, columnCount, lastIndexOf, lastDataIndex = -1;
47    long /*int*/ ignoreTextCell, ignorePixbufCell;
48    TableItem [] items;
49    TableColumn [] columns;
50    ImageList imageList;
51    boolean firstCustomDraw;
52    
53    static final int CHECKED_COLUMN = 0;
54    static final int GRAYED_COLUMN = 1;
55    static final int FOREGROUND_COLUMN = 2;
56    static final int BACKGROUND_COLUMN = 3;
57    static final int FONT_COLUMN = 4;
58    static final int FIRST_COLUMN = FONT_COLUMN + 1;
59  
60  /**
61   * Constructs a new instance of this class given its parent
62   * and a style value describing its behavior and appearance.
63   * <p>
64   * The style value is either one of the style constants defined in
65   * class <code>SWT</code> which is applicable to instances of this
66   * class, or must be built by <em>bitwise OR</em>'ing together 
67   * (that is, using the <code>int</code> "|" operator) two or more
68   * of those <code>SWT</code> style constants. The class description
69   * lists the style constants that are applicable to the class.
70   * Style bits are also inherited from superclasses.
71   * </p>
72   *
73   * @param parent a composite control which will be the parent of the new instance (cannot be null)
74   * @param style the style of control to construct
75   *
76   * @exception IllegalArgumentException <ul>
77   *    <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
78   * </ul>
79   * @exception SWTException <ul>
80   *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
81   *    <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
82   * </ul>
83   *
84   * @see SWT#SINGLE
85   * @see SWT#MULTI
86   * @see SWT#CHECK
87   * @see SWT#FULL_SELECTION
88   * @see SWT#HIDE_SELECTION
89   * @see Widget#checkSubclass
90   * @see Widget#getStyle
91   */
92  public Table (Composite parent, int style) {
93    super (parent, checkStyle (style));
94  }
95  
96  TableItem _getItem (int index) {
97    if (items [index] != null) return items [index];
98    return items [index] = new TableItem (this, SWT.NONE, index, false);
99  }
100 
101 static int checkStyle (int style) {
102   /*
103   * To be compatible with Windows, force the H_SCROLL
104   * and V_SCROLL style bits.  On Windows, it is not
105   * possible to create a table without scroll bars.
106   */
107   style |= SWT.H_SCROLL | SWT.V_SCROLL;
108   return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0);
109 }
110 
111 /**
112  * Adds the listener to the collection of listeners who will
113  * be notified when the receiver's selection changes, by sending
114  * it one of the messages defined in the <code>SelectionListener</code>
115  * interface.
116  * <p>
117  * When <code>widgetSelected</code> is called, the item field of the event object is valid.
118  * If the reciever has <code>SWT.CHECK</code> style set and the check selection changes,
119  * the event object detail field contains the value <code>SWT.CHECK</code>.
120  * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
121  * The item field of the event object is valid for default selection, but the detail field is not used.
122  * </p>
123  *
124  * @param listener the listener which should be notified
125  *
126  * @exception IllegalArgumentException <ul>
127  *    <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
128  * </ul>
129  * @exception SWTException <ul>
130  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
131  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
132  * </ul>
133  *
134  * @see SelectionListener
135  * @see #removeSelectionListener
136  * @see SelectionEvent
137  */
138 public void addSelectionListener (SelectionListener listener) {
139   checkWidget();
140   if (listener == null) error (SWT.ERROR_NULL_ARGUMENT);
141   TypedListener typedListener = new TypedListener (listener);
142   addListener (SWT.Selection,typedListener);
143   addListener (SWT.DefaultSelection,typedListener);
144 }
145 
146 long /*int*/ textCellDataProc (long /*int*/ tree_column, long /*int*/ cell, long /*int*/ tree_model, long /*int*/ iter, long /*int*/ data) {
147   if (cell == ignoreTextCell) return 0;
148   int modelIndex = -1;
149   boolean customDraw = false;
150   if (columnCount == 0) {
151     modelIndex = Table.FIRST_COLUMN;
152     customDraw = firstCustomDraw;
153   } else {
154     for (int i = 0; i < columns.length; i++) {
155       if (columns [i] != null && columns [i].handle == tree_column) {
156         modelIndex = columns [i].modelIndex;
157         customDraw = columns [i].customDraw;
158         break;
159       }
160     }
161   }
162   if (modelIndex == -1) return 0;
163   boolean setData = setCellData (tree_model, iter);
164   long /*int*/ [] ptr = new long /*int*/ [1];
165   if (setData) {
166     OS.gtk_tree_model_get (tree_model, iter, modelIndex + 1, ptr, -1); //text
167     if (ptr [0] != 0) {
168       OS.g_object_set(cell, OS.text, ptr[0], 0);
169       OS.g_free (ptr[0]);
170     }
171     ptr = new long /*int*/ [1];
172   }
173   if (customDraw) {
174     OS.gtk_tree_model_get (tree_model, iter, modelIndex + 2, ptr, -1); //foreground-gdk
175     if (ptr [0] != 0) {
176       OS.g_object_set(cell, OS.foreground_gdk, ptr[0], 0);
177     }
178     ptr = new long /*int*/ [1];
179     OS.gtk_tree_model_get (tree_model, iter, modelIndex + 3, ptr, -1); //background-gdk
180     if (ptr [0] != 0) {
181       OS.g_object_set(cell, OS.background_gdk, ptr[0], 0);
182     }
183     ptr = new long /*int*/ [1];
184     OS.gtk_tree_model_get (tree_model, iter, modelIndex + 4, ptr, -1); //font-desc
185     if (ptr [0] != 0) {
186       OS.g_object_set(cell, OS.font_desc, ptr[0], 0);
187     }
188   }
189   if (setData) {
190     ignoreTextCell = cell;
191     setScrollWidth (tree_column, iter);
192     ignoreTextCell = 0;
193   }
194   return 0;
195 }
196 long /*int*/ pixbufCellDataProc (long /*int*/ tree_column, long /*int*/ cell, long /*int*/ tree_model, long /*int*/ iter, long /*int*/ data) {
197   if (cell == ignorePixbufCell) return 0;
198   int modelIndex = -1;
199   boolean customDraw = false;
200   if (columnCount == 0) {
201     modelIndex = Table.FIRST_COLUMN;
202     customDraw = firstCustomDraw;
203   } else {
204     for (int i = 0; i < columns.length; i++) {
205       if (columns [i] != null && columns [i].handle == tree_column) {
206         modelIndex = columns [i].modelIndex;
207         customDraw = columns [i].customDraw;
208         break;
209       }
210     }
211   }
212   if (modelIndex == -1) return 0;
213   boolean setData = setCellData (tree_model, iter);
214   long /*int*/ [] ptr = new long /*int*/ [1];
215   if (setData) {
216     OS.gtk_tree_model_get (tree_model, iter, modelIndex, ptr, -1); //pixbuf
217     OS.g_object_set(cell, OS.pixbuf, ptr[0], 0);
218     ptr = new long /*int*/ [1];
219   }
220   if (customDraw) {
221     OS.gtk_tree_model_get (tree_model, iter, modelIndex + 3, ptr, -1); //cell-background-gdk
222     if (ptr [0] != 0) {
223       OS.g_object_set(cell, OS.cell_background_gdk, ptr[0], 0);
224     }
225   }
226   if (setData) {
227     ignorePixbufCell = cell;
228     setScrollWidth (tree_column, iter);
229     ignorePixbufCell = 0;
230   }
231   return 0;
232 }
233 
234 int calculateWidth (long /*int*/ column, long /*int*/ iter) {
235   OS.gtk_tree_view_column_cell_set_cell_data (column, modelHandle, iter, false, false);
236   int [] width = new int [1];
237   OS.gtk_tree_view_column_cell_get_size (column, null, null, null, width, null);
238   return width [0];
239 }
240 
241 public Point computeSize (int wHint, int hHint, boolean changed) {
242   checkWidget ();
243   if (wHint != SWT.DEFAULT && wHint < 0) wHint = 0;
244   if (hHint != SWT.DEFAULT && hHint < 0) hHint = 0;
245   Point size = computeNativeSize(handle, wHint, hHint, changed);
246   Rectangle trim = computeTrim (0, 0, size.x, size.y);
247   size.x = trim.width;
248   size.y = trim.height;
249   return size;
250 }
251 
252 /**
253  * Clears the item at the given zero-relative index in the receiver.
254  * The text, icon and other attribues of the item are set to the default
255  * value.  If the table was created with the SWT.VIRTUAL style, these
256  * attributes are requested again as needed.
257  *
258  * @param index the index of the item to clear
259  *
260  * @exception IllegalArgumentException <ul>
261  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
262  * </ul>
263  * @exception SWTException <ul>
264  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
265  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
266  * </ul>
267  * 
268  * @see SWT#VIRTUAL
269  * @see SWT#SetData
270  * 
271  * @since 3.0
272  */
273 public void clear (int index) {
274   checkWidget ();
275   if (!(0 <= index && index < itemCount)) {
276     error(SWT.ERROR_INVALID_RANGE);
277   }
278   TableItem item = items [index];
279   if (item != null) item.clear();
280 }
281 
282 /**
283  * Removes the items from the receiver which are between the given
284  * zero-relative start and end indices (inclusive).  The text, icon
285  * and other attribues of the items are set to their default values.
286  * If the table was created with the SWT.VIRTUAL style, these attributes
287  * are requested again as needed.
288  *
289  * @param start the start index of the item to clear
290  * @param end the end index of the item to clear
291  *
292  * @exception IllegalArgumentException <ul>
293  *    <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li>
294  * </ul>
295  * @exception SWTException <ul>
296  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
297  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
298  * </ul>
299  * 
300  * @see SWT#VIRTUAL
301  * @see SWT.SetData
302  * 
303  * @since 3.0
304  */
305 public void clear (int start, int end) {
306   checkWidget ();
307   if (start > end) return;
308   if (!(0 <= start && start <= end && end < itemCount)) {
309     error (SWT.ERROR_INVALID_RANGE);
310   }
311   if (start == 0 && end == itemCount - 1) {
312     clearAll();
313   } else {
314     for (int i=start; i<=end; i++) {
315       TableItem item = items [i];
316       if (item != null) item.clear();
317     }
318   }
319 }
320 
321 /**
322  * Clears the items at the given zero-relative indices in the receiver.
323  * The text, icon and other attribues of the items are set to their default
324  * values.  If the table was created with the SWT.VIRTUAL style, these
325  * attributes are requested again as needed.
326  *
327  * @param indices the array of indices of the items
328  *
329  * @exception IllegalArgumentException <ul>
330  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
331  *    <li>ERROR_NULL_ARGUMENT - if the indices array is null</li>
332  * </ul>
333  * @exception SWTException <ul>
334  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
335  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
336  * </ul>
337  * 
338  * @see SWT#VIRTUAL
339  * @see SWT.SetData
340  * 
341  * @since 3.0
342  */
343 public void clear (int [] indices) {
344   checkWidget ();
345   if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
346   if (indices.length == 0) return;
347   for (int i=0; i<indices.length; i++) {
348     if (!(0 <= indices [i] && indices [i] < itemCount)) {
349       error (SWT.ERROR_INVALID_RANGE);
350     }
351   }
352   for (int i=0; i<indices.length; i++) {
353     TableItem item = items [indices [i]];
354     if (item != null) item.clear();
355   }
356 }
357 
358 /**
359  * Clears all the items in the receiver. The text, icon and other
360  * attribues of the items are set to their default values. If the
361  * table was created with the SWT.VIRTUAL style, these attributes
362  * are requested again as needed.
363  *
364  * @exception SWTException <ul>
365  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
366  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
367  * </ul>
368  * 
369  * @see SWT#VIRTUAL
370  * @see SWT.SetData
371  * 
372  * @since 3.0
373  */
374 public void clearAll () {
375   checkWidget ();
376   for (int i=0; i<itemCount; i++) {
377     TableItem item = items [i];
378     if (item != null) item.clear();
379   }
380 }
381 
382 void createHandle (int index) {
383   state |= HANDLE;
384   fixedHandle = OS.gtk_fixed_new ();
385   if (fixedHandle == 0) error (SWT.ERROR_NO_HANDLES);
386   OS.gtk_fixed_set_has_window (fixedHandle, true);
387   scrolledHandle = OS.gtk_scrolled_window_new (0, 0);
388   if (scrolledHandle == 0) error (SWT.ERROR_NO_HANDLES);
389   /*
390   * Columns:
391   * 0 - check
392   * 1 - grayed
393   * 2 - foreground for row
394   * 3 - background for row
395     * 4 - font for row
396   * 5 - pixbuf for cell
397   * 6 - text for cell
398   * 7 - foreground for cell
399   * 8 - background for cell
400   * 9 - font for cell
401   * 10 - ... 
402   */
403   long /*int*/ [] types = getColumnTypes (1);
404   modelHandle = OS.gtk_list_store_newv (types.length, types);
405   if (modelHandle == 0) error (SWT.ERROR_NO_HANDLES);
406   handle = OS.gtk_tree_view_new_with_model (modelHandle);
407   if (handle == 0) error (SWT.ERROR_NO_HANDLES);
408   if ((style & SWT.CHECK) != 0) {
409     checkRenderer = OS.gtk_cell_renderer_toggle_new ();
410     if (checkRenderer == 0) error (SWT.ERROR_NO_HANDLES);
411     OS.g_object_ref (checkRenderer);
412   }
413   createColumn (null, 0);
414   long /*int*/ parentHandle = parent.parentingHandle ();
415   OS.gtk_container_add (parentHandle, fixedHandle);
416   OS.gtk_container_add (fixedHandle, scrolledHandle);
417   OS.gtk_container_add (scrolledHandle, handle);
418   OS.gtk_widget_show (fixedHandle);
419   OS.gtk_widget_show (scrolledHandle);
420   OS.gtk_widget_show (handle);
421   
422   int mode = (style & SWT.MULTI) != 0 ? OS.GTK_SELECTION_MULTIPLE : OS.GTK_SELECTION_BROWSE;
423   long /*int*/ selectionHandle = OS.gtk_tree_view_get_selection (handle);
424   OS.gtk_tree_selection_set_mode (selectionHandle, mode);
425   OS.gtk_tree_view_set_headers_visible (handle, false);  
426   int hsp = (style & SWT.H_SCROLL) != 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER;
427   int vsp = (style & SWT.V_SCROLL) != 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER;
428   OS.gtk_scrolled_window_set_policy (scrolledHandle, hsp, vsp);
429   if ((style & SWT.BORDER) != 0) OS.gtk_scrolled_window_set_shadow_type (scrolledHandle, OS.GTK_SHADOW_ETCHED_IN);
430   if ((style & SWT.VIRTUAL) != 0) {
431     /*
432     * Feature in GTK. The fixed_height_mode property only exists in GTK 2.3.2 and greater.
433     */
434     if (OS.gtk_major_version () * 100 + OS.gtk_minor_version () * 10 + OS.gtk_micro_version () >= 232) {
435       OS.g_object_set (handle, OS.fixed_height_mode, true, 0);
436     }
437   }
438 }
439 
440 void createColumn (TableColumn column, int index) {
441   int modelIndex = FIRST_COLUMN;
442   if (columnCount != 0) {
443     int modelLength = OS.gtk_tree_model_get_n_columns (modelHandle);
444     boolean [] usedColumns = new boolean [modelLength];
445     for (int i=0; i<columnCount; i++) {
446       int columnIndex = columns [i].modelIndex;
447       usedColumns [columnIndex] = usedColumns [columnIndex + 1] = usedColumns [columnIndex + 2] = usedColumns [columnIndex + 3] = usedColumns [columnIndex + 4] = true;
448     }
449     while (modelIndex < modelLength) {
450       if (!usedColumns [modelIndex]) break;
451       modelIndex++;
452     }
453     if (modelIndex == modelLength) {
454       long /*int*/ oldModel = modelHandle;
455       long /*int*/[] types = getColumnTypes (columnCount + 5);
456       long /*int*/ newModel = OS.gtk_list_store_newv (types.length, types);
457       if (newModel == 0) error (SWT.ERROR_NO_HANDLES);
458       long /*int*/ [] ptr = new long /*int*/ [1];
459       for (int i=0; i<itemCount; i++) {
460         long /*int*/ newItem = OS.g_malloc (OS.GtkTreeIter_sizeof ());
461         if (newItem == 0) error (SWT.ERROR_NO_HANDLES);
462         OS.gtk_list_store_append (newModel, newItem);
463         TableItem item = items [i];
464         if (item != null) {
465           long /*int*/ oldItem = item.handle;
466           for (int j=0; j<modelLength; j++) {
467             OS.gtk_tree_model_get (oldModel, oldItem, j, ptr, -1);
468             OS.gtk_list_store_set (newModel, newItem, j, ptr [0], -1);
469             if (types [j] == OS.G_TYPE_STRING ()) OS.g_free ((ptr [0]));
470           }
471           OS.gtk_list_store_remove (oldModel, oldItem);
472           OS.g_free (oldItem);
473           item.handle = newItem;
474         } else {
475           OS.g_free (newItem);
476         }
477       }
478       OS.gtk_tree_view_set_model (handle, newModel);
479       OS.g_object_unref (oldModel);
480       modelHandle = newModel;
481     }
482   }
483   long /*int*/ columnHandle = OS.gtk_tree_view_column_new ();
484   if (columnHandle == 0) error (SWT.ERROR_NO_HANDLES);
485   if (index == 0 && columnCount > 0) {
486     TableColumn checkColumn = columns [0];
487     createRenderers (checkColumn.handle, checkColumn.modelIndex, false, checkColumn.style);
488   }
489   createRenderers (columnHandle, modelIndex, index == 0, column == null ? 0 : column.style);
490   if ((style & SWT.VIRTUAL) == 0 && columnCount == 0) {
491     OS.gtk_tree_view_column_set_sizing (columnHandle, OS.GTK_TREE_VIEW_COLUMN_AUTOSIZE);
492   } else {
493     OS.gtk_tree_view_column_set_sizing (columnHandle, OS.GTK_TREE_VIEW_COLUMN_FIXED);
494     OS.gtk_tree_view_column_set_fixed_width (columnHandle, 10);
495   }
496   OS.gtk_tree_view_column_set_resizable (columnHandle, true);
497   OS.gtk_tree_view_column_set_clickable (columnHandle, true);
498   OS.gtk_tree_view_insert_column (handle, columnHandle, index);
499   if (column != null) {
500     column.handle = columnHandle;
501     column.modelIndex = modelIndex;
502   }
503 }
504 
505 void createRenderers (long /*int*/ columnHandle, int modelIndex, boolean check, int columnStyle) {
506   OS.gtk_tree_view_column_clear (columnHandle);
507   if ((style & SWT.CHECK) != 0 && check) {
508     OS.gtk_tree_view_column_pack_start (columnHandle, checkRenderer, false);
509     OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, "active", CHECKED_COLUMN);
510     OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, "cell-background-gdk", BACKGROUND_COLUMN);
511     
512     /*
513     * Feature in GTK. The inconsistent property only exists in GTK 2.2.x.
514     */
515     if (OS.gtk_major_version () > 2 || (OS.gtk_major_version () == 2 && OS.gtk_minor_version () >= 2)) {
516       OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, "inconsistent", GRAYED_COLUMN);
517     }
518   }
519   long /*int*/ pixbufRenderer = OS.gtk_cell_renderer_pixbuf_new ();
520   if (pixbufRenderer == 0) error (SWT.ERROR_NO_HANDLES);
521   long /*int*/ textRenderer = OS.gtk_cell_renderer_text_new ();
522   if (textRenderer == 0) error (SWT.ERROR_NO_HANDLES);
523   
524   /*
525   * Feature on GTK.  When a tree view column contains only one activatable
526   * cell renderer such as a toggle renderer, mouse clicks anywhere in a cell
527   * activate that renderer. The workaround is to set a second  cell renderer
528   * to be activatable.
529   */
530   if ((style & SWT.CHECK) != 0 && check) {
531     OS.g_object_set (pixbufRenderer, OS.mode, OS.GTK_CELL_RENDERER_MODE_ACTIVATABLE, 0);
532   }
533 
534   /* Set alignment */
535   if ((columnStyle & SWT.RIGHT) != 0) {
536     OS.g_object_set (textRenderer, OS.xalign, 1f, 0);
537     OS.gtk_tree_view_column_pack_start (columnHandle, pixbufRenderer, false);
538     OS.gtk_tree_view_column_pack_start (columnHandle, textRenderer, true);
539     OS.gtk_tree_view_column_set_alignment (columnHandle, 1f);
540   } else if ((columnStyle & SWT.CENTER) != 0) {
541     OS.g_object_set (textRenderer, OS.xalign, 0.5f, 0);
542     OS.gtk_tree_view_column_pack_start (columnHandle, pixbufRenderer, false);
543     OS.gtk_tree_view_column_pack_end (columnHandle, textRenderer, true);
544     OS.gtk_tree_view_column_set_alignment (columnHandle, 0.5f);
545   } else {
546     OS.gtk_tree_view_column_pack_start (columnHandle, pixbufRenderer, false);
547     OS.gtk_tree_view_column_pack_start (columnHandle, textRenderer, true);
548     OS.gtk_tree_view_column_set_alignment (columnHandle, 0f);
549   }
550 
551   /* Add attributes */
552   OS.gtk_tree_view_column_add_attribute (columnHandle, pixbufRenderer, "pixbuf", modelIndex);
553   OS.gtk_tree_view_column_add_attribute (columnHandle, pixbufRenderer, "cell-background-gdk", BACKGROUND_COLUMN);
554   OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, "text", modelIndex + 1);
555   OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, "foreground-gdk", FOREGROUND_COLUMN);
556   OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, "background-gdk", BACKGROUND_COLUMN);
557   OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, "font-desc", FONT_COLUMN);
558   
559   boolean customDraw = firstCustomDraw;
560   if (columnCount != 0) {
561     for (int i=0; i<columnCount; i++) {
562       if (columns [i].handle == columnHandle) {
563         customDraw = columns [i].customDraw;
564         break;
565       }
566     }
567   }
568   if ((style & SWT.VIRTUAL) != 0 || customDraw) {
569     OS.gtk_tree_view_column_set_cell_data_func (columnHandle, textRenderer, display.textCellDataProc, handle, 0);
570     OS.gtk_tree_view_column_set_cell_data_func (columnHandle, pixbufRenderer, display.pixbufCellDataProc, handle, 0);
571   }
572 }
573 
574 void createItem (TableColumn column, int index) {
575   if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE);
576   if (columnCount == 0) {
577     column.handle = OS.gtk_tree_view_get_column (handle, 0);
578     OS.gtk_tree_view_column_set_sizing (column.handle, OS.GTK_TREE_VIEW_COLUMN_FIXED);
579     OS.gtk_tree_view_column_set_fixed_width (column.handle, 10);
580     column.modelIndex = FIRST_COLUMN;
581     createRenderers (column.handle, column.modelIndex, true, column.style);
582     column.customDraw = firstCustomDraw;
583     firstCustomDraw = false;
584   } else {
585     createColumn (column, index);
586   }
587   long /*int*/ boxHandle = OS.gtk_hbox_new (false, 3);
588   if (boxHandle == 0) error (SWT.ERROR_NO_HANDLES);
589   long /*int*/ labelHandle = OS.gtk_label_new_with_mnemonic (null);
590   if (labelHandle == 0) error (SWT.ERROR_NO_HANDLES);
591   long /*int*/ imageHandle = OS.gtk_image_new ();
592   if (imageHandle == 0) error (SWT.ERROR_NO_HANDLES);
593   OS.gtk_container_add (boxHandle, imageHandle);
594   OS.gtk_container_add (boxHandle, labelHandle);
595   OS.gtk_widget_show (boxHandle);
596   OS.gtk_widget_show (labelHandle);
597   column.boxHandle = boxHandle;
598   column.labelHandle = labelHandle;
599   column.imageHandle = imageHandle;  
600   OS.gtk_tree_view_column_set_widget (column.handle, boxHandle);
601   long /*int*/ widget = OS.gtk_widget_get_parent (boxHandle);
602   while (widget != handle) {
603     if (OS.GTK_IS_BUTTON (widget)) {
604       column.buttonHandle = widget;
605       break;
606     }
607     widget = OS.gtk_widget_get_parent (widget);
608   }
609   if (columnCount == columns.length) {
610     TableColumn [] newColumns = new TableColumn [columns.length + 4];
611     System.arraycopy (columns, 0, newColumns, 0, columns.length);
612     columns = newColumns;
613   }
614   System.arraycopy (columns, index, columns, index + 1, columnCount++ - index);
615   columns [index] = column;
616   column.setFontDescription (getFontDescription ());
617 }
618 
619 void createItem (TableItem item, int index) {
620   if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE);
621   if (itemCount == items.length) {
622     int length = drawCount == 0 ? items.length + 4 : Math.max (4, items.length * 3 / 2);
623     TableItem [] newItems = new TableItem [length];  
624     System.arraycopy (items, 0, newItems, 0, items.length);
625     items = newItems;
626   }
627   item.handle = OS.g_malloc (OS.GtkTreeIter_sizeof ());
628   if (item.handle == 0) error (SWT.ERROR_NO_HANDLES);
629   if (index == itemCount) {
630     OS.gtk_list_store_append (modelHandle, item.handle);
631   } else {
632     OS.gtk_list_store_insert (modelHandle, item.handle, index);
633   }
634   System.arraycopy (items, index, items, index + 1, itemCount++ - index);
635   items [index] = item;
636 }
637 
638 void createWidget (int index) {
639   super.createWidget (index);
640   items = new TableItem [4];
641   columns = new TableColumn [4];
642   itemCount = columnCount = 0;
643 }
644 
645 void deregister() {
646   super.deregister ();
647   display.removeWidget (OS.gtk_tree_view_get_selection (handle));
648   if (checkRenderer != 0) display.removeWidget (checkRenderer);
649 }
650 
651 /**
652  * Deselects the item at the given zero-relative index in the receiver.
653  * If the item at the index was already deselected, it remains
654  * deselected. Indices that are out of range are ignored.
655  *
656  * @param index the index of the item to deselect
657  *
658  * @exception SWTException <ul>
659  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
660  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
661  * </ul>
662  */
663 public void deselect (int index) {
664   checkWidget();
665   if (index < 0 || index >= itemCount) return;
666   long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
667   OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
668   OS.gtk_tree_selection_unselect_iter (selection, _getItem (index).handle);
669   OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
670 }
671 
672 /**
673  * Deselects the items at the given zero-relative indices in the receiver.
674  * If the item at the given zero-relative index in the receiver 
675  * is selected, it is deselected.  If the item at the index
676  * was not selected, it remains deselected.  The range of the
677  * indices is inclusive. Indices that are out of range are ignored.
678  *
679  * @param start the start index of the items to deselect
680  * @param end the end index of the items to deselect
681  *
682  * @exception SWTException <ul>
683  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
684  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
685  * </ul>
686  */
687 public void deselect (int start, int end) {
688   checkWidget();
689   long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
690   OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
691   for (int index=start; index<=end; index++) {
692     if (index < 0 || index >= itemCount) continue;
693     OS.gtk_tree_selection_unselect_iter (selection, _getItem (index).handle);
694   }
695   OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
696 }
697 
698 /**
699  * Deselects the items at the given zero-relative indices in the receiver.
700  * If the item at the given zero-relative index in the receiver 
701  * is selected, it is deselected.  If the item at the index
702  * was not selected, it remains deselected. Indices that are out
703  * of range and duplicate indices are ignored.
704  *
705  * @param indices the array of indices for the items to deselect
706  *
707  * @exception IllegalArgumentException <ul>
708  *    <li>ERROR_NULL_ARGUMENT - if the set of indices is null</li>
709  * </ul>
710  * @exception SWTException <ul>
711  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
712  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
713  * </ul>
714  */
715 public void deselect (int [] indices) {
716   checkWidget();
717   if (indices == null) error (SWT.ERROR_NULL_ARGUMENT);
718   long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
719   OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
720   for (int i=0; i<indices.length; i++) {
721     int index = indices[i];
722     if (index < 0 || index >= itemCount) continue;
723     OS.gtk_tree_selection_unselect_iter (selection, _getItem (index).handle);
724   }
725   OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
726 }
727 
728 /**
729  * Deselects all selected items in the receiver.
730  *
731  * @exception SWTException <ul>
732  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
733  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
734  * </ul>
735  */
736 public void deselectAll () {
737   checkWidget();
738   long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
739   OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
740   OS.gtk_tree_selection_unselect_all (selection);
741   OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
742 }
743 
744 void destroyItem (TableColumn column) {
745   int index = 0;
746   while (index < columnCount) {
747     if (columns [index] == column) break;
748     index++;
749   }
750   if (index == columnCount) return;
751   long /*int*/ columnHandle = column.handle;
752   if (columnCount == 1) {
753     firstCustomDraw = column.customDraw;
754   }
755   System.arraycopy (columns, index + 1, columns, index, --columnCount - index);
756   columns [columnCount] = null;
757   column.deregister ();
758   OS.gtk_tree_view_remove_column (handle, columnHandle);
759   if (columnCount == 0) {
760     long /*int*/ oldModel = modelHandle;
761     long /*int*/[] types = getColumnTypes (1);
762     long /*int*/ newModel = OS.gtk_list_store_newv (types.length, types);
763     if (newModel == 0) error (SWT.ERROR_NO_HANDLES);
764     long /*int*/ [] ptr = new long /*int*/ [1];
765     for (int i=0; i<itemCount; i++) {
766       long /*int*/ newItem = OS.g_malloc (OS.GtkTreeIter_sizeof ());
767       if (newItem == 0) error (SWT.ERROR_NO_HANDLES);
768       OS.gtk_list_store_append (newModel, newItem);
769       TableItem item = items [i];
770       if (item != null) {
771         long /*int*/ oldItem = item.handle;
772         for (int j=0; j<FIRST_COLUMN; j++) {
773           OS.gtk_tree_model_get (oldModel, oldItem, j, ptr, -1);
774           OS.gtk_list_store_set (newModel, newItem, j, ptr [0], -1);
775         }
776         OS.gtk_tree_model_get (oldModel, oldItem, column.modelIndex, ptr, -1); //image
777         OS.gtk_list_store_set (newModel, newItem, FIRST_COLUMN, ptr [0], -1);
778         OS.gtk_tree_model_get (oldModel, oldItem, column.modelIndex + 1, ptr, -1); //text
779         OS.gtk_list_store_set (newModel, newItem, FIRST_COLUMN + 1, ptr [0], -1);
780         OS.g_free (ptr [0]);
781         OS.gtk_tree_model_get (oldModel, oldItem, column.modelIndex + 2, ptr, -1); //foreground
782         OS.gtk_list_store_set (newModel, newItem, FIRST_COLUMN + 2, ptr [0], -1);
783         OS.gtk_tree_model_get (oldModel, oldItem, column.modelIndex + 3, ptr, -1); //background
784         OS.gtk_list_store_set (newModel, newItem, FIRST_COLUMN + 3, ptr [0], -1);
785         OS.gtk_tree_model_get (oldModel, oldItem, column.modelIndex + 4, ptr, -1); //font
786         OS.gtk_list_store_set (newModel, newItem, FIRST_COLUMN + 4, ptr [0], -1);
787         OS.gtk_list_store_remove (oldModel, oldItem);
788         OS.g_free (oldItem);
789         item.handle = newItem;
790       } else {
791         OS.g_free (newItem);
792       }
793     }
794     OS.gtk_tree_view_set_model (handle, newModel);
795     OS.g_object_unref (oldModel);
796     modelHandle = newModel;
797     createColumn (null, 0);
798   } else {
799     for (int i=0; i<itemCount; i++) {
800       TableItem item = items [i];
801       if (item != null) {
802         long /*int*/ iter = item.handle;
803         int modelIndex = column.modelIndex;
804         OS.gtk_list_store_set (modelHandle, iter, modelIndex, 0, -1); //image
805         OS.gtk_list_store_set (modelHandle, iter, modelIndex + 1, 0, -1); //text
806         OS.gtk_list_store_set (modelHandle, iter, modelIndex + 2, 0, -1); //foreground
807         OS.gtk_list_store_set (modelHandle, iter, modelIndex + 3, 0, -1); //background
808         OS.gtk_list_store_set (modelHandle, iter, modelIndex + 4, 0, -1); //font
809       }
810     }
811     if (index == 0) {
812       TableColumn checkColumn = columns [0];
813       createRenderers (checkColumn.handle, checkColumn.modelIndex, true, checkColumn.style);
814     }
815   }
816   column.handle = column.buttonHandle = column.labelHandle = 0;
817   column.boxHandle = column.imageHandle = 0;
818 }
819 
820 void destroyItem (TableItem item) {
821   int index = 0;
822   while (index < itemCount) {
823     if (items [index] == item) break;
824     index++;
825   }
826   if (index == itemCount) return;
827   long /*int*/ itemHandle = item.handle;
828   long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
829   OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
830   OS.gtk_list_store_remove (modelHandle, itemHandle);
831   OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
832   OS.g_free (itemHandle);
833   item.handle = 0;
834   System.arraycopy (items, index + 1, items, index, --itemCount - index);
835   items [itemCount] = null;
836   lastDataIndex = -1;
837   if (itemCount == 0) resetCustomDraw ();
838 }
839 
840 GdkColor getBackgroundColor () {
841   return getBaseColor ();
842 }
843 
844 /**
845  * Returns the column at the given, zero-relative index in the
846  * receiver. Throws an exception if the index is out of range.
847  * If no <code>TableColumn</code>s were created by the programmer,
848  * this method will throw <code>ERROR_INVALID_RANGE</code> despite
849  * the fact that a single column of data may be visible in the table.
850  * This occurs when the programmer uses the table like a list, adding
851  * items but never creating a column.
852  *
853  * @param index the index of the column to return
854  * @return the column at the given index
855  *
856  * @exception IllegalArgumentException <ul>
857  *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
858  * </ul>
859  * @exception SWTException <ul>
860  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
861  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
862  * </ul>
863  */
864 public TableColumn getColumn (int index) {
865   checkWidget();
866   if (!(0 <= index && index < columnCount)) error (SWT.ERROR_CANNOT_GET_ITEM);
867   return columns [index];
868 }
869 
870 /**
871  * Returns the number of columns contained in the receiver.
872  * If no <code>TableColumn</code>s were created by the programmer,
873  * this value is zero, despite the fact that visually, one column
874  * of items is may be visible. This occurs when the programmer uses
875  * the table like a list, adding items but never creating a column.
876  *
877  * @return the number of columns
878  *
879  * @exception SWTException <ul>
880  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
881  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
882  * </ul>
883  * @exception SWTError <ul>
884  *    <li>ERROR_CANNOT_GET_COUNT - if the operation fails because of an operating system failure</li>
885  * </ul>
886  */
887 public int getColumnCount () {
888   checkWidget();
889   return columnCount;
890 }
891 
892 long /*int*/[] getColumnTypes (int n) {
893   long /*int*/[] types = new long /*int*/ [(n * 5) + FIRST_COLUMN];
894   types [CHECKED_COLUMN] = OS.G_TYPE_BOOLEAN ();
895   types [GRAYED_COLUMN] = OS.G_TYPE_BOOLEAN ();
896   types [FOREGROUND_COLUMN] = OS.GDK_TYPE_COLOR ();
897   types [BACKGROUND_COLUMN] = OS.GDK_TYPE_COLOR ();
898   types [FONT_COLUMN] = OS.PANGO_TYPE_FONT_DESCRIPTION ();
899   for (int i=FIRST_COLUMN; i<types.length; i+=5) {
900     types [i + 0] = OS.GDK_TYPE_PIXBUF (); //image
901     types [i + 1] = OS.G_TYPE_STRING (); //text
902     types [i + 2] = OS.GDK_TYPE_COLOR (); //foreground
903     types [i + 3] = OS.GDK_TYPE_COLOR (); //background
904     types [i + 4] = OS.PANGO_TYPE_FONT_DESCRIPTION (); //font
905   }
906   return types;
907 }
908 
909 /**
910  * Returns an array of <code>TableColumn</code>s which are the
911  * columns in the receiver. If no <code>TableColumn</code>s were
912  * created by the programmer, the array is empty, despite the fact
913  * that visually, one column of items may be visible. This occurs
914  * when the programmer uses the table like a list, adding items but
915  * never creating a column.
916  * <p>
917  * Note: This is not the actual structure used by the receiver
918  * to maintain its list of items, so modifying the array will
919  * not affect the receiver. 
920  * </p>
921  *
922  * @return the items in the receiver
923  *
924  * @exception SWTException <ul>
925  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
926  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
927  * </ul>
928  */
929 public TableColumn [] getColumns () {
930   checkWidget();
931   TableColumn [] result = new TableColumn [columnCount];
932   System.arraycopy (columns, 0, result, 0, columnCount);
933   return result;
934 }
935 
936 TableItem getFocusItem () {
937   long /*int*/ [] path = new long /*int*/ [1];
938   OS.gtk_tree_view_get_cursor (handle, path, null);
939   if (path [0] == 0) return null;
940   TableItem item = null;
941   long /*int*/ indices = OS.gtk_tree_path_get_indices (path [0]);
942   if (indices != 0) {
943     int [] index = new int []{-1};
944     OS.memmove (index, indices, 4);
945     item = _getItem (index [0]); 
946   }
947   OS.gtk_tree_path_free (path [0]);
948   return item;
949 } 
950 
951 GdkColor getForegroundColor () {
952   return getTextColor ();
953 }
954 
955 /**
956  * Returns the width in pixels of a grid line.
957  *
958  * @return the width of a grid line in pixels
959  * 
960  * @exception SWTException <ul>
961  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
962  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
963  * </ul>
964  */
965 public int getGridLineWidth () {
966   checkWidget();
967   return 0;
968 }
969 
970 /**
971  * Returns the height of the receiver's header 
972  *
973  * @return the height of the header or zero if the header is not visible
974  *
975  * @exception SWTException <ul>
976  *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
977  *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
978  * </ul>
979  * 
980  * @since 2.0 
981  */
982 public int getHeaderHeight () {
983   checkWidget ();
984   if (!OS.gtk_tree_view_get_headers_visible (handle)) return 0;
985   OS.gtk_widget_realize (handle);
986   long /*int*/ fixedWindow = OS.GTK_WIDGET_WINDOW (fixedHandle);
987   long /*int*/ binWindow = OS.gtk_tree_view_get_bin_window (handle);
988   int [] binY = new int [1];
989   OS.gdk_window_get_origin (binWindow, null, binY);
990   int [] fixedY = new int [1];
991   OS.gdk_window_get_origin (fixedWindow, null, fixedY);
992   return binY [0] - fixedY [0];
993 }
994 
995 /**
996  * Returns <code>true</code> if the receiver's header is visible,
997  * and <code>false</code> otherwise.
998  * <p>
999  * If one of the receiver's ancestors is not visible or some
1000 * other condition makes the receiver not visible, this method
1001 * may still indicate that it is considered visible even though
1002 * it may not actually be showing.
1003 * </p>
1004 *
1005 * @return the receiver's header's visibility state
1006 *
1007 * @exception SWTException <ul>
1008 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1009 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1010 * </ul>
1011 */
1012public boolean getHeaderVisible () {
1013  checkWidget();
1014  return OS.gtk_tree_view_get_headers_visible (handle);
1015}
1016
1017/**
1018 * Returns the item at the given, zero-relative index in the
1019 * receiver. Throws an exception if the index is out of range.
1020 *
1021 * @param index the index of the item to return
1022 * @return the item at the given index
1023 *
1024 * @exception IllegalArgumentException <ul>
1025 *    <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li>
1026 * </ul>
1027 * @exception SWTException <ul>
1028 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1029 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1030 * </ul>
1031 */
1032public TableItem getItem (int index) {
1033  checkWidget();
1034  if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE);
1035  return _getItem (index);
1036}
1037
1038/**
1039 * Returns the item at the given point in the receiver
1040 * or null if no such item exists. The point is in the
1041 * coordinate system of the receiver.
1042 *
1043 * @param point the point used to locate the item
1044 * @return the item at the given point
1045 *
1046 * @exception IllegalArgumentException <ul>
1047 *    <li>ERROR_NULL_ARGUMENT - if the point is null</li>
1048 * </ul>
1049 * @exception SWTException <ul>
1050 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1051 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1052 * </ul>
1053 */
1054public TableItem getItem (Point pt) {
1055  checkWidget();
1056  long /*int*/ [] path = new long /*int*/ [1];
1057  int clientX = pt.x - getBorderWidth ();
1058  int clientY = pt.y - getHeaderHeight ();
1059  OS.gtk_widget_realize (handle);
1060  if (!OS.gtk_tree_view_get_path_at_pos (handle, clientX, clientY, path, null, null, null)) return null;
1061  if (path [0] == 0) return null;
1062  long /*int*/ indices = OS.gtk_tree_path_get_indices (path [0]);
1063  TableItem item = null;
1064  if (indices != 0) {
1065    int [] index = new int [1];
1066    OS.memmove (index, indices, 4);
1067    item = _getItem (index [0]);
1068  }
1069  OS.gtk_tree_path_free (path [0]);
1070  return item;
1071}
1072
1073/**
1074 * Returns the number of items contained in the receiver.
1075 *
1076 * @return the number of items
1077 *
1078 * @exception SWTException <ul>
1079 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1080 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1081 * </ul>
1082 */
1083public int getItemCount () {
1084  checkWidget ();
1085  return itemCount;
1086}
1087
1088/**
1089 * Returns the height of the area which would be used to
1090 * display <em>one</em> of the items in the receiver's.
1091 *
1092 * @return the height of one item
1093 *
1094 * @exception SWTException <ul>
1095 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1096 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1097 * </ul>
1098 */
1099public int getItemHeight () {
1100  checkWidget();
1101  if (itemCount == 0) {
1102    long /*int*/ column = OS.gtk_tree_view_get_column (handle, 0);
1103    int [] w = new int [1], h = new int [1];
1104    OS.gtk_tree_view_column_cell_get_size (column, null, null, null, w, h);
1105    return h [0];
1106  } else {
1107    int height = 0;
1108    long /*int*/ iter = OS.g_malloc (OS.GtkTreeIter_sizeof ());
1109    OS.gtk_tree_model_get_iter_first (modelHandle, iter);
1110    int columnCount = Math.max (1, this.columnCount);
1111    for (int i=0; i<columnCount; i++) {
1112      long /*int*/ column = OS.gtk_tree_view_get_column (handle, i);
1113      OS.gtk_tree_view_column_cell_set_cell_data (column, modelHandle, iter, false, false);
1114      int [] w = new int [1], h = new int [1];
1115      OS.gtk_tree_view_column_cell_get_size (column, null, null, null, w, h);
1116      height = Math.max (height, h [0]);
1117    }
1118    OS.g_free (iter);
1119    return height;
1120  }
1121}
1122
1123/**
1124 * Returns an array of <code>TableItem</code>s which are the items
1125 * in the receiver. 
1126 * <p>
1127 * Note: This is not the actual structure used by the receiver
1128 * to maintain its list of items, so modifying the array will
1129 * not affect the receiver. 
1130 * </p>
1131 *
1132 * @return the items in the receiver
1133 *
1134 * @exception SWTException <ul>
1135 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1136 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1137 * </ul>
1138 */
1139public TableItem [] getItems () {
1140  checkWidget();
1141  TableItem [] result = new TableItem [itemCount];
1142  if ((style & SWT.VIRTUAL) != 0) {
1143    for (int i=0; i<itemCount; i++) {
1144      result [i] = _getItem (i);
1145    }
1146  } else {
1147    System.arraycopy (items, 0, result, 0, itemCount);
1148  }
1149  return result;
1150}
1151
1152/**
1153 * Returns <code>true</code> if the receiver's lines are visible,
1154 * and <code>false</code> otherwise.
1155 * <p>
1156 * If one of the receiver's ancestors is not visible or some
1157 * other condition makes the receiver not visible, this method
1158 * may still indicate that it is considered visible even though
1159 * it may not actually be showing.
1160 * </p>
1161 *
1162 * @return the visibility state of the lines
1163 *
1164 * @exception SWTException <ul>
1165 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1166 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1167 * </ul>
1168 */
1169public boolean getLinesVisible() {
1170  checkWidget();
1171  return OS.gtk_tree_view_get_rules_hint (handle);
1172}
1173
1174/**
1175 * Returns an array of <code>TableItem</code>s that are currently
1176 * selected in the receiver. An empty array indicates that no
1177 * items are selected.
1178 * <p>
1179 * Note: This is not the actual structure used by the receiver
1180 * to maintain its selection, so modifying the array will
1181 * not affect the receiver. 
1182 * </p>
1183 * @return an array representing the selection
1184 *
1185 * @exception SWTException <ul>
1186 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1187 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1188 * </ul>
1189 */
1190public TableItem [] getSelection () {
1191  checkWidget();
1192  display.treeSelectionLength  = 0;
1193  display.treeSelection = new int [itemCount];
1194  long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
1195  OS.gtk_tree_selection_selected_foreach (selection, display.treeSelectionProc, handle);
1196  TableItem [] result = new TableItem [display.treeSelectionLength];
1197  for (int i=0; i<result.length; i++) result [i] = _getItem (display.treeSelection [i]);
1198  return result;
1199}
1200
1201/**
1202 * Returns the number of selected items contained in the receiver.
1203 *
1204 * @return the number of selected items
1205 *
1206 * @exception SWTException <ul>
1207 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1208 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1209 * </ul>
1210 */
1211public int getSelectionCount () {
1212  checkWidget();
1213  display.treeSelectionLength = 0;
1214  display.treeSelection = null;
1215  long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
1216  OS.gtk_tree_selection_selected_foreach (selection, display.treeSelectionProc, handle);
1217  return display.treeSelectionLength;
1218}
1219
1220/**
1221 * Returns the zero-relative index of the item which is currently
1222 * selected in the receiver, or -1 if no item is selected.
1223 *
1224 * @return the index of the selected item
1225 *
1226 * @exception SWTException <ul>
1227 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1228 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1229 * </ul>
1230 */
1231public int getSelectionIndex () {
1232  checkWidget();
1233  display.treeSelectionLength  = 0;
1234  display.treeSelection = new int [itemCount];
1235  long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
1236  OS.gtk_tree_selection_selected_foreach (selection, display.treeSelectionProc, handle);
1237  if (display.treeSelectionLength == 0) return -1;
1238  return display.treeSelection [0];
1239}
1240
1241/**
1242 * Returns the zero-relative indices of the items which are currently
1243 * selected in the receiver.  The array is empty if no items are selected.
1244 * <p>
1245 * Note: This is not the actual structure used by the receiver
1246 * to maintain its selection, so modifying the array will
1247 * not affect the receiver. 
1248 * </p>
1249 * @return the array of indices of the selected items
1250 *
1251 * @exception SWTException <ul>
1252 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1253 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1254 * </ul>
1255 */
1256public int [] getSelectionIndices () {
1257  checkWidget();
1258  display.treeSelectionLength  = 0;
1259  display.treeSelection = new int [itemCount];
1260  long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
1261  OS.gtk_tree_selection_selected_foreach (selection, display.treeSelectionProc, handle);
1262  if (display.treeSelectionLength == display.treeSelection.length) return display.treeSelection;
1263  int [] result = new int [display.treeSelectionLength];
1264  System.arraycopy (display.treeSelection, 0, result, 0, display.treeSelectionLength);
1265  return result;
1266}
1267
1268/**
1269 * Returns the zero-relative index of the item which is currently
1270 * at the top of the receiver. This index can change when items are
1271 * scrolled or new items are added or removed.
1272 *
1273 * @return the index of the top item
1274 *
1275 * @exception SWTException <ul>
1276 *    <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
1277 *    <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
1278 * </ul>
1279 */
1280public int getTopIndex () {
1281  checkWidget();
1282  long /*int*/ [] path = new long /*int*/ [1];
1283  OS.gtk_widget_realize (handle);
1284  if (!OS.gtk_tree_view_get_path_at_pos (handle, 1, 1, path, null, null, null)) return 0;
1285  if (path [0] == 0) return 0;
1286  long /*int*/ indices = OS.gtk_tree_path_get_indices (path[0]);
1287  int[] index = new int [1];
1288  if (indices != 0) OS.memmove (index, indices, 4);
1289  OS.gtk_tree_path_free (path [0]);
1290  return index [0];
1291}
1292
1293long /*int*/ gtk_button_press_event (long /*int*/ widget, long /*int*/ event) {
1294  GdkEventButton gdkEvent = new GdkEventButton ();
1295  OS.memmove (gdkEvent, event, GdkEventButton.sizeof);
1296  if (gdkEvent.window != OS.gtk_tree_view_get_bin_window (handle)) return 0;
1297  /*
1298  * Bug in GTK. GTK segments fault, if the GtkTreeView widget is
1299  * not in focus and all items in the widget are disposed before
1300  * it finishes processing a button press.  The fix is to give
1301  * focus to the widget before it starts processing the event.
1302  */
1303  if (!OS.GTK_WIDGET_HAS_FOCUS (handle)) {
1304    OS.gtk_widget_grab_focus (handle);
1305    // widget may be disposed at this point
1306    if (isDisposed ()) return 0;
1307  }
1308  int border = getBorderWidth ();
1309  int headerHeight = getHeaderHeight ();
1310  gdkEvent.x += border;
1311  gdkEvent.y += headerHeight;
1312  OS.memmove (event, gdkEvent, GdkEventButton.sizeof);
1313  long /*int*/ result = super.gtk_button_press_event (widget, event);
1314  gdkEvent.x -= border;
1315  gdkEvent.y -= headerHeight;
1316  OS.memmove (event, gdkEvent, GdkEventButton.sizeof);
1317  if (result != 0) return result;
1318  /*
1319  * Feature in GTK.  In a multi-select table view, when multiple items are already
1320  * selected, the selection state of the item is toggled and the previous selection 
1321  * is cleared. This is not the desired behaviour when bringing up a popup menu.
1322  * Also, when an item is reselected with the right button, the tree view issues
1323  * an unwanted selection event. The workaround is to detect that case and not
1324  * run the default handler when the item is already part of the current selection.
1325  */
1326  int button = gdkEvent.button;
1327  if (button == 3 && gdkEvent.type == OS.GDK_BUTTON_PRESS) {
1328    long /*int*/ [] path = new long /*int*/ [1];
1329    if (OS.gtk_tree_view_get_path_at_pos (handle, (int)gdkEvent.x, (int)gdkEvent.y, path, null, null, null)) {
1330      if (path [0] != 0) {
1331        long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
1332        if (OS.gtk_tree_selection_path_is_selected (selection, path [0])) result = 1;
1333        OS.gtk_tree_path_free (path [0]);
1334      }
1335    }
1336  }
1337  
1338  /*
1339  * Feature in GTK.  When the user clicks in a single selection GtkTreeView
1340  * and there are no selected items, the first item is selected automatically
1341  * before the click is processed, causing two selection events.  The is fix
1342  * is the set the cursor item to be same as the clicked item to stop the
1343  * widget from automatically selecting the first item.
1344  */
1345  if ((style & SWT.SINGLE) != 0 && getSelectionCount () == 0) {
1346    long /*int*/ [] path = new long /*int*/ [1];
1347    if (OS.gtk_tree_view_get_path_at_pos (handle, (int)gdkEvent.x, (int)gdkEvent.y, path, null, null, null)) {
1348      if (path [0] != 0) {
1349        long /*int*/ selection = OS.gtk_tree_view_get_selection (handle);
1350        OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
1351        OS.gtk_tree_view_set_cursor (handle, path [0], 0, false);
1352        OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, 0, 0, CHANGED);
1353        OS.gtk_tree_path_free (path [0]);
1354      }
1355    }
1356  }
1357  return result;
1358}
1359
1360long /*int*/ gtk_button_release_event (long /*int*/ widget, long /*int*/ event) {
1361  GdkEventButton gdkEvent = new GdkEventButton ();
1362  OS.memmove (gdkEvent, event, GdkEventButton.sizeof);
1363  if (gdkEvent.window != OS.gtk_tree_view_get_bin_window (handle)) return 0;
1364  int border = getBorderWidth ();
1365  int headerHeight = getHeaderHeight ();
1366  gdkEvent.x += border;
1367  gdkEvent.y += headerHeight;
1368  OS.memmove (event, gdkEvent, GdkEventButton.sizeof);
1369  long /*int*/ result = super.gtk_button_release_event (widget, event);
1370  gdkEvent.x -= border;
1371  gdkEvent.y -= headerHeight;
1372  OS.memmove (event, gdkEvent, GdkEventButton.sizeof);
1373  return result;
1374}
1375
1376long /*int*/ gtk_changed (long /*int*/ widget) {
1377  TableItem item = getFocusItem ();
1378  if (item != null) {
1379    Event event = new Event ();
1380    event.item = item; 
1381    postEvent (SWT.Selection, event);
1382  }
1383  return 0;
1384}
1385
1386long /*int*/ gtk_key_press_event (long /*int*/ widget, long /*int*/ eventPtr) {
1387  long /*int*/ result = super.gtk_key_press_event (widget, eventPtr);
1388  if (result != 0) return result;
1389
1390  /*
1391  * Feature in GTK.  When an item is default selected using
1392  * the return key, GTK does not issue notification. The fix is
1393  * to issue this notification when the return key is pressed.
1394  */
1395  GdkEventKey keyEvent = new GdkEventKey ();
1396  OS.memmove (keyEvent, eventPtr, GdkEventKey.sizeof);
1397  int key = keyEvent.keyval;
1398  switch (key) {
1399    case OS.GDK_Return:
1400    case OS.GDK_KP_Enter: {
1401      Event event = new Event ();
1402      event.item = getFocusItem (); 
1403      postEvent (SWT.DefaultSelection, event);
1404      break;
1405    }
1406  }
1407  return result;
1408}
1409
1410long /*int*/ gtk_motion_notify_event (long /*int*/ widget, long /*int*/ event) {
1411  GdkEventButton gdkEvent = new GdkEventButton ();
1412  OS.memmove (gdkEvent, event, GdkEventButton.sizeof);
1413  if (gdkEvent.window != OS.gtk_tree_view_get_bin_window (handle)) return 0;
1414  int border = getBorderWidth ();
1415  int headerHeight = getHeaderHeight ();
1416  gdkEvent.x += border;
1417  gdkEvent.y += headerHeight;
1418  OS.memmove (event, gdkEvent, GdkEventButton.sizeof);
1419  long /*int*/ result = super.gtk_motion_notify_event (widget, event);
1420  gdkEvent.x -= border;
1421  gdkEvent.y -= headerHeight;
1422  OS.memmove (event, gdkEvent, GdkEventButton.sizeof);
1423  return result;
1424}
1425
1426long /*int*/ gtk_row_activated (long /*int*/ tree, long /*int*/ path, long /*int*/ column) {
1427  TableItem item = null;
1428  long /*int*/ indices = OS.gtk_tree_path_get_indices (path);
1429  if (indices != 0) {
1430    int [] index = new int []{-1};
1431    OS.memmove (index, indices, 4);
1432    item = _getItem (index [0]); 
1433  }
1434  Event event = new Event ();
1435  event.item = item;
1436  postEvent (SWT.DefaultSelection, event);
1437  return 0;
1438}
1439
1440long /*int*/ gtk_toggled (long /*int*/ renderer, long /*int*/ pathStr) {
1441  long /*int*/ path = OS.gtk_tree_path_new_from_string (pathStr);
1442  if (path == 0) return 0;
1443  long /*int*/ indices = OS.gtk_tree_path_get_indices (path);
1444  if (indices != 0) {
1445    int [] index = new int [1];
1446    OS.memmove (index, indices, 4);
1447    TableItem item = _getItem (index [0]);
1448    item.setChecked (!item.getChecked ());
1449    Event event = new Event ();
1450    event.detail = SWT.CHECK;
1451    event.item = item;
1452    postEvent (SWT.Selection, event);
1453  }
1454  OS.gtk_tree_path_free (path);
1455  return 0;
1456}
1457