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