1 /******************************************************************************* 2 * Copyright (c) 2000, 2008 IBM Corporation and others. 3 * All rights reserved. This program and the accompanying materials 4 * are made available under the terms of the Eclipse Public License v1.0 5 * which accompanies this distribution, and is available at 6 * http://www.eclipse.org/legal/epl-v10.html 7 * 8 * Contributors: 9 * IBM Corporation - initial API and implementation 10 * Port to the D programming language: 11 * Frank Benoit <benoit@tionex.de> 12 *******************************************************************************/ 13 module org.eclipse.swt.widgets.Table; 14 15 16 17 import org.eclipse.swt.SWT; 18 import org.eclipse.swt.SWTException; 19 import org.eclipse.swt.events.SelectionEvent; 20 import org.eclipse.swt.events.SelectionListener; 21 import org.eclipse.swt.graphics.Color; 22 import org.eclipse.swt.graphics.Font; 23 import org.eclipse.swt.graphics.GC; 24 import org.eclipse.swt.graphics.Image; 25 import org.eclipse.swt.graphics.Point; 26 import org.eclipse.swt.graphics.Rectangle; 27 import org.eclipse.swt.internal.gtk.OS; 28 import org.eclipse.swt.widgets.Composite; 29 import org.eclipse.swt.widgets.TableItem; 30 import org.eclipse.swt.widgets.TableColumn; 31 import org.eclipse.swt.widgets.Listener; 32 import org.eclipse.swt.widgets.ImageList; 33 import org.eclipse.swt.widgets.Shell; 34 import org.eclipse.swt.widgets.Decorations; 35 import org.eclipse.swt.widgets.Menu; 36 import org.eclipse.swt.widgets.Display; 37 import org.eclipse.swt.widgets.ScrollBar; 38 import org.eclipse.swt.widgets.Event; 39 import org.eclipse.swt.widgets.Control; 40 import org.eclipse.swt.widgets.TypedListener; 41 import java.lang.all; 42 43 version(Tango){ 44 import tango.util.Convert; 45 } else { // Phobos 46 import std.conv; 47 } 48 49 /** 50 * Instances of this class implement a selectable user interface 51 * object that displays a list of images and strings and issues 52 * notification when selected. 53 * <p> 54 * The item children that may be added to instances of this class 55 * must be of type <code>TableItem</code>. 56 * </p><p> 57 * Style <code>VIRTUAL</code> is used to create a <code>Table</code> whose 58 * <code>TableItem</code>s are to be populated by the client on an on-demand basis 59 * instead of up-front. This can provide significant performance improvements for 60 * tables that are very large or for which <code>TableItem</code> population is 61 * expensive (for example, retrieving values from an external source). 62 * </p><p> 63 * Here is an example of using a <code>Table</code> with style <code>VIRTUAL</code>: 64 * <code><pre> 65 * final Table table = new Table (parent, SWT.VIRTUAL | SWT.BORDER); 66 * table.setItemCount (1000000); 67 * table.addListener (SWT.SetData, new Listener () { 68 * public void handleEvent (Event event) { 69 * TableItem item = (TableItem) event.item; 70 * int index = table.indexOf (item); 71 * item.setText ("Item " + index); 72 * System.out.println (item.getText ()); 73 * } 74 * }); 75 * </pre></code> 76 * </p><p> 77 * Note that although this class is a subclass of <code>Composite</code>, 78 * it does not normally make sense to add <code>Control</code> children to 79 * it, or set a layout on it, unless implementing something like a cell 80 * editor. 81 * </p><p> 82 * <dl> 83 * <dt><b>Styles:</b></dt> 84 * <dd>SINGLE, MULTI, CHECK, FULL_SELECTION, HIDE_SELECTION, VIRTUAL, NO_SCROLL</dd> 85 * <dt><b>Events:</b></dt> 86 * <dd>Selection, DefaultSelection, SetData, MeasureItem, EraseItem, PaintItem</dd> 87 * </dl> 88 * </p><p> 89 * Note: Only one of the styles SINGLE, and MULTI may be specified. 90 * </p><p> 91 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 92 * </p> 93 * 94 * @see <a href="http://www.eclipse.org/swt/snippets/#table">Table, TableItem, TableColumn snippets</a> 95 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 96 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 97 */ 98 public class Table : Composite { 99 100 alias Composite.computeSize computeSize; 101 alias Composite.createHandle createHandle; 102 alias Composite.dragDetect dragDetect; 103 alias Composite.mnemonicHit mnemonicHit; 104 alias Composite.mnemonicMatch mnemonicMatch; 105 alias Composite.setBackgroundColor setBackgroundColor; 106 alias Composite.setBounds setBounds; 107 108 CallbackData treeSelectionCallbackData; 109 GtkWidget* modelHandle, checkRenderer; 110 int itemCount, columnCount, lastIndexOf, sortDirection; 111 GtkWidget* ignoreCell; 112 TableItem [] items; 113 TableColumn [] columns; 114 TableItem currentItem; 115 TableColumn sortColumn; 116 ImageList imageList, headerImageList; 117 bool firstCustomDraw; 118 int drawState, drawFlags; 119 GdkColor* drawForeground; 120 bool ownerDraw, ignoreSize; 121 122 static const int CHECKED_COLUMN = 0; 123 static const int GRAYED_COLUMN = 1; 124 static const int FOREGROUND_COLUMN = 2; 125 static const int BACKGROUND_COLUMN = 3; 126 static const int FONT_COLUMN = 4; 127 static const int FIRST_COLUMN = FONT_COLUMN + 1; 128 static const int CELL_PIXBUF = 0; 129 static const int CELL_TEXT = 1; 130 static const int CELL_FOREGROUND = 2; 131 static const int CELL_BACKGROUND = 3; 132 static const int CELL_FONT = 4; 133 static const int CELL_TYPES = CELL_FONT + 1; 134 135 /** 136 * Constructs a new instance of this class given its parent 137 * and a style value describing its behavior and appearance. 138 * <p> 139 * The style value is either one of the style constants defined in 140 * class <code>SWT</code> which is applicable to instances of this 141 * class, or must be built by <em>bitwise OR</em>'ing together 142 * (that is, using the <code>int</code> "|" operator) two or more 143 * of those <code>SWT</code> style constants. The class description 144 * lists the style constants that are applicable to the class. 145 * Style bits are also inherited from superclasses. 146 * </p> 147 * 148 * @param parent a composite control which will be the parent of the new instance (cannot be null) 149 * @param style the style of control to construct 150 * 151 * @exception IllegalArgumentException <ul> 152 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 153 * </ul> 154 * @exception SWTException <ul> 155 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 156 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 157 * </ul> 158 * 159 * @see SWT#SINGLE 160 * @see SWT#MULTI 161 * @see SWT#CHECK 162 * @see SWT#FULL_SELECTION 163 * @see SWT#HIDE_SELECTION 164 * @see SWT#VIRTUAL 165 * @see SWT#NO_SCROLL 166 * @see Widget#checkSubclass 167 * @see Widget#getStyle 168 */ 169 public this (Composite parent, int style) { 170 super (parent, checkStyle (style)); 171 } 172 173 override void _addListener (int eventType, Listener listener) { 174 super._addListener (eventType, listener); 175 if (!ownerDraw) { 176 switch (eventType) { 177 case SWT.MeasureItem: 178 case SWT.EraseItem: 179 case SWT.PaintItem: 180 ownerDraw = true; 181 recreateRenderers (); 182 break; 183 default: 184 } 185 } 186 } 187 188 TableItem _getItem (int index) { 189 if ((style & SWT.VIRTUAL) is 0) return items [index]; 190 if (items [index] !is null) return items [index]; 191 return items [index] = new TableItem (this, SWT.NONE, index, false); 192 } 193 194 static int checkStyle (int style) { 195 /* 196 * Feature in Windows. Even when WS_HSCROLL or 197 * WS_VSCROLL is not specified, Windows creates 198 * trees and tables with scroll bars. The fix 199 * is to set H_SCROLL and V_SCROLL. 200 * 201 * NOTE: This code appears on all platforms so that 202 * applications have consistent scroll bar behavior. 203 */ 204 if ((style & SWT.NO_SCROLL) is 0) { 205 style |= SWT.H_SCROLL | SWT.V_SCROLL; 206 } 207 /* GTK is always FULL_SELECTION */ 208 style |= SWT.FULL_SELECTION; 209 return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); 210 } 211 212 override void cellDataProc ( 213 GtkTreeViewColumn *tree_column, 214 GtkCellRenderer *cell, 215 GtkTreeModel *tree_model, 216 GtkTreeIter *iter, 217 void* data) 218 { 219 if (cell is cast(GtkCellRenderer*)ignoreCell) return; 220 auto path = OS.gtk_tree_model_get_path (tree_model, iter); 221 auto index = OS.gtk_tree_path_get_indices (path); 222 TableItem item = _getItem (index[0]); 223 OS.gtk_tree_path_free (path); 224 if (item !is null) OS.g_object_set_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX2, item.handle); 225 bool isPixbuf = OS.GTK_IS_CELL_RENDERER_PIXBUF (cast(GTypeInstance*)cell); 226 if (!(isPixbuf || OS.GTK_IS_CELL_RENDERER_TEXT (cast(GTypeInstance*)cell))) return; 227 int modelIndex = -1; 228 bool customDraw = false; 229 if (columnCount is 0) { 230 modelIndex = Table.FIRST_COLUMN; 231 customDraw = firstCustomDraw; 232 } else { 233 TableColumn column = cast(TableColumn) display.getWidget (cast(GtkWidget*)tree_column); 234 if (column !is null) { 235 modelIndex = column.modelIndex; 236 customDraw = column.customDraw; 237 } 238 } 239 if (modelIndex is -1) return; 240 bool setData = false; 241 if ((style & SWT.VIRTUAL) !is 0) { 242 /* 243 * Feature in GTK. On GTK before 2.4, fixed_height_mode is not 244 * supported, and the tree asks for the data of all items. The 245 * fix is to only provide the data if the row is visible. 246 */ 247 if (OS.GTK_VERSION < OS.buildVERSION (2, 3, 2)) { 248 OS.gtk_widget_realize (handle); 249 GdkRectangle visible; 250 OS.gtk_tree_view_get_visible_rect (handle, &visible); 251 GdkRectangle area; 252 path = OS.gtk_tree_model_get_path (tree_model, iter); 253 OS.gtk_tree_view_get_cell_area (handle, path, tree_column, &area); 254 OS.gtk_tree_path_free (path); 255 if (area.y + area.height < 0 || area.y + visible.y > visible.y + visible.height) { 256 /* Give an image from the image list to make sure the row has 257 * the correct height. 258 */ 259 if (imageList !is null && imageList.pixbufs.length > 0) { 260 if (isPixbuf) OS.g_object_set1 (cell, OS.pixbuf.ptr, cast(int)imageList.pixbufs [0] ); 261 } 262 return; 263 } 264 } 265 if (!item.cached) { 266 lastIndexOf = index[0]; 267 setData = checkData (item); 268 } 269 } 270 void* ptr; 271 if (setData) { 272 if (isPixbuf) { 273 OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_PIXBUF, &ptr); 274 OS.g_object_set1 (cell, OS.pixbuf.ptr, cast(int)ptr); 275 } else { 276 OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_TEXT, &ptr); 277 if (ptr !is null) { 278 OS.g_object_set1 (cell, OS.text.ptr, cast(int)ptr); 279 OS.g_free (ptr); 280 } 281 } 282 } 283 if (customDraw) { 284 /* 285 * Bug on GTK. Gtk renders the background on top of the checkbox and pixbuf. 286 * This only happens in version 2.2.1 and earlier. The fix is not to set the background. 287 */ 288 if (OS.GTK_VERSION > OS.buildVERSION (2, 2, 1)) { 289 if (!ownerDraw) { 290 ptr = null; 291 OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_BACKGROUND, &ptr); 292 if (ptr !is null) { 293 OS.g_object_set1 (cell, OS.cell_background_gdk.ptr, cast(int)ptr); 294 } 295 } 296 } 297 if (!isPixbuf) { 298 ptr = null; 299 OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_FOREGROUND, &ptr); 300 if (ptr !is null) { 301 OS.g_object_set1 (cell, OS.foreground_gdk.ptr, cast(int)ptr); 302 } 303 ptr = null; 304 OS.gtk_tree_model_get1 (tree_model, iter, modelIndex + CELL_FONT, &ptr); 305 if (ptr !is null) { 306 OS.g_object_set1 (cell, OS.font_desc.ptr, cast(int)ptr); 307 } 308 } 309 } 310 if (setData) { 311 ignoreCell = cast(GtkWidget*)cell; 312 setScrollWidth (tree_column, item); 313 ignoreCell = null; 314 } 315 return; 316 } 317 318 bool checkData (TableItem item) { 319 if (item.cached) return true; 320 if ((style & SWT.VIRTUAL) !is 0) { 321 item.cached = true; 322 Event event = new Event (); 323 event.item = item; 324 event.index = indexOf (item); 325 int mask = OS.G_SIGNAL_MATCH_DATA | OS.G_SIGNAL_MATCH_ID; 326 int signal_id = OS.g_signal_lookup (OS.row_changed.ptr, OS.gtk_tree_model_get_type ()); 327 OS.g_signal_handlers_block_matched (modelHandle, mask, signal_id, 0, null, null, handle); 328 currentItem = item; 329 sendEvent (SWT.SetData, event); 330 //widget could be disposed at this point 331 currentItem = null; 332 if (isDisposed ()) return false; 333 OS.g_signal_handlers_unblock_matched (modelHandle, mask, signal_id, 0, null, null, handle); 334 if (item.isDisposed ()) return false; 335 } 336 return true; 337 } 338 339 protected override void checkSubclass () { 340 if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS); 341 } 342 343 /** 344 * Adds the listener to the collection of listeners who will 345 * be notified when the user changes the receiver's selection, by sending 346 * it one of the messages defined in the <code>SelectionListener</code> 347 * interface. 348 * <p> 349 * When <code>widgetSelected</code> is called, the item field of the event object is valid. 350 * If the receiver has the <code>SWT.CHECK</code> style and the check selection changes, 351 * the event object detail field contains the value <code>SWT.CHECK</code>. 352 * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. 353 * The item field of the event object is valid for default selection, but the detail field is not used. 354 * </p> 355 * 356 * @param listener the listener which should be notified when the user changes the receiver's selection 357 * 358 * @exception IllegalArgumentException <ul> 359 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 360 * </ul> 361 * @exception SWTException <ul> 362 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 363 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 364 * </ul> 365 * 366 * @see SelectionListener 367 * @see #removeSelectionListener 368 * @see SelectionEvent 369 */ 370 public void addSelectionListener (SelectionListener listener) { 371 checkWidget (); 372 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 373 TypedListener typedListener = new TypedListener (listener); 374 addListener (SWT.Selection, typedListener); 375 addListener (SWT.DefaultSelection, typedListener); 376 } 377 378 int calculateWidth (GtkTreeViewColumn* column, GtkTreeIter* iter) { 379 OS.gtk_tree_view_column_cell_set_cell_data (column, modelHandle, iter, false, false); 380 /* 381 * Bug in GTK. The width calculated by gtk_tree_view_column_cell_get_size() 382 * always grows in size regardless of the text or images in the table. 383 * The fix is to determine the column width from the cell renderers. 384 */ 385 // Code intentionally commented 386 //int [] width = new int [1]; 387 //OS.gtk_tree_view_column_cell_get_size (column, null, null, null, width, null); 388 //return width [0]; 389 390 int width = 0; 391 int w; 392 OS.gtk_widget_style_get1(handle, OS.focus_line_width.ptr, &w ); 393 width += 2 * w; 394 auto list = OS.gtk_tree_view_column_get_cell_renderers (column); 395 if (list is null) return 0; 396 auto temp = list; 397 while (temp !is null) { 398 auto renderer = OS.g_list_data (temp); 399 if (renderer !is null) { 400 OS.gtk_cell_renderer_get_size (renderer, handle, null, null, null, &w, null); 401 width += w; 402 } 403 temp = OS.g_list_next (temp); 404 } 405 OS.g_list_free (list); 406 return width; 407 } 408 409 /** 410 * Clears the item at the given zero-relative index in the receiver. 411 * The text, icon and other attributes of the item are set to the default 412 * value. If the table was created with the <code>SWT.VIRTUAL</code> style, 413 * these attributes are requested again as needed. 414 * 415 * @param index the index of the item to clear 416 * 417 * @exception IllegalArgumentException <ul> 418 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 419 * </ul> 420 * @exception SWTException <ul> 421 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 422 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 423 * </ul> 424 * 425 * @see SWT#VIRTUAL 426 * @see SWT#SetData 427 * 428 * @since 3.0 429 */ 430 public void clear (int index) { 431 checkWidget (); 432 if (!(0 <= index && index < itemCount)) { 433 error(SWT.ERROR_INVALID_RANGE); 434 } 435 TableItem item = items [index]; 436 if (item !is null) item.clear (); 437 } 438 439 /** 440 * Removes the items from the receiver which are between the given 441 * zero-relative start and end indices (inclusive). The text, icon 442 * and other attributes of the items are set to their default values. 443 * If the table was created with the <code>SWT.VIRTUAL</code> style, 444 * these attributes are requested again as needed. 445 * 446 * @param start the start index of the item to clear 447 * @param end the end index of the item to clear 448 * 449 * @exception IllegalArgumentException <ul> 450 * <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> 451 * </ul> 452 * @exception SWTException <ul> 453 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 454 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 455 * </ul> 456 * 457 * @see SWT#VIRTUAL 458 * @see SWT#SetData 459 * 460 * @since 3.0 461 */ 462 public void clear (int start, int end) { 463 checkWidget (); 464 if (start > end) return; 465 if (!(0 <= start && start <= end && end < itemCount)) { 466 error (SWT.ERROR_INVALID_RANGE); 467 } 468 if (start is 0 && end is itemCount - 1) { 469 clearAll(); 470 } else { 471 for (int i=start; i<=end; i++) { 472 TableItem item = items [i]; 473 if (item !is null) item.clear(); 474 } 475 } 476 } 477 478 /** 479 * Clears the items at the given zero-relative indices in the receiver. 480 * The text, icon and other attributes of the items are set to their default 481 * values. If the table was created with the <code>SWT.VIRTUAL</code> style, 482 * these attributes are requested again as needed. 483 * 484 * @param indices the array of indices of the items 485 * 486 * @exception IllegalArgumentException <ul> 487 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 488 * </ul> 489 * @exception SWTException <ul> 490 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 491 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 492 * </ul> 493 * 494 * @see SWT#VIRTUAL 495 * @see SWT#SetData 496 * 497 * @since 3.0 498 */ 499 public void clear (int [] indices) { 500 checkWidget (); 501 // SWT extension: allow null for zero length string 502 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 503 if (indices.length is 0) return; 504 for (int i=0; i<indices.length; i++) { 505 if (!(0 <= indices [i] && indices [i] < itemCount)) { 506 error (SWT.ERROR_INVALID_RANGE); 507 } 508 } 509 for (int i=0; i<indices.length; i++) { 510 TableItem item = items [indices [i]]; 511 if (item !is null) item.clear(); 512 } 513 } 514 515 /** 516 * Clears all the items in the receiver. The text, icon and other 517 * attributes of the items are set to their default values. If the 518 * table was created with the <code>SWT.VIRTUAL</code> style, these 519 * attributes are requested again as needed. 520 * 521 * @exception SWTException <ul> 522 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 523 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 524 * </ul> 525 * 526 * @see SWT#VIRTUAL 527 * @see SWT#SetData 528 * 529 * @since 3.0 530 */ 531 public void clearAll () { 532 checkWidget (); 533 for (int i=0; i<itemCount; i++) { 534 TableItem item = items [i]; 535 if (item !is null) item.clear(); 536 } 537 } 538 539 public override Point computeSize (int wHint, int hHint, bool changed) { 540 checkWidget (); 541 if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0; 542 if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0; 543 Point size = computeNativeSize (handle, wHint, hHint, changed); 544 Rectangle trim = computeTrim (0, 0, size.x, size.y); 545 size.x = trim.width; 546 size.y = trim.height; 547 return size; 548 } 549 550 void createColumn (TableColumn column, int index) { 551 int modelIndex = FIRST_COLUMN; 552 if (columnCount !is 0) { 553 int modelLength = OS.gtk_tree_model_get_n_columns (modelHandle); 554 bool [] usedColumns = new bool [modelLength]; 555 for (int i=0; i<columnCount; i++) { 556 int columnIndex = columns [i].modelIndex; 557 for (int j = 0; j < CELL_TYPES; j++) { 558 usedColumns [columnIndex + j] = true; 559 } 560 } 561 while (modelIndex < modelLength) { 562 if (!usedColumns [modelIndex]) break; 563 modelIndex++; 564 } 565 if (modelIndex is modelLength) { 566 auto oldModel = modelHandle; 567 auto types = getColumnTypes (columnCount + 4); // grow by 4 rows at a time 568 auto newModel = OS.gtk_list_store_newv 569 (cast(int)/*64bit*/types.length, types.ptr); 570 if (newModel is null) error (SWT.ERROR_NO_HANDLES); 571 void* ptr; 572 for (int i=0; i<itemCount; i++) { 573 GtkTreeIter* newItem = cast(GtkTreeIter*)OS.g_malloc( GtkTreeIter.sizeof ); 574 if (newItem is null) error (SWT.ERROR_NO_HANDLES); 575 OS.gtk_list_store_append (newModel, newItem); 576 TableItem item = items [i]; 577 if (item !is null) { 578 auto oldItem = item.handle; 579 for (int j=0; j<modelLength; j++) { 580 OS.gtk_tree_model_get1 (oldModel, oldItem, j, &ptr); 581 OS.gtk_list_store_set1 (newModel, newItem, j, ptr); 582 if (types [j] is OS.G_TYPE_STRING ()) OS.g_free ((ptr)); 583 } 584 OS.gtk_list_store_remove (oldModel, oldItem); 585 OS.g_free (oldItem); 586 item.handle = cast(GtkWidget*)newItem; 587 } else { 588 OS.g_free (newItem); 589 } 590 } 591 OS.gtk_tree_view_set_model (handle, newModel); 592 OS.g_object_unref (oldModel); 593 modelHandle = cast(GtkWidget*)newModel; 594 } 595 } 596 auto columnHandle = OS.gtk_tree_view_column_new (); 597 if (columnHandle is null) error (SWT.ERROR_NO_HANDLES); 598 if (index is 0 && columnCount > 0) { 599 TableColumn checkColumn = columns [0]; 600 createRenderers (cast(GtkTreeViewColumn*)checkColumn.handle, checkColumn.modelIndex, false, checkColumn.style); 601 } 602 createRenderers ( columnHandle, modelIndex, index is 0, column is null ? 0 : column.style); 603 /* 604 * Use GTK_TREE_VIEW_COLUMN_GROW_ONLY on GTK versions < 2.3.2 605 * because fixed_height_mode is not supported. 606 */ 607 bool useVirtual = (style & SWT.VIRTUAL) !is 0 && OS.GTK_VERSION >= OS.buildVERSION (2, 3, 2); 608 if (!useVirtual && columnCount is 0) { 609 OS.gtk_tree_view_column_set_sizing (columnHandle, OS.GTK_TREE_VIEW_COLUMN_GROW_ONLY); 610 } else { 611 OS.gtk_tree_view_column_set_sizing (columnHandle, OS.GTK_TREE_VIEW_COLUMN_FIXED); 612 if (columnCount !is 0) OS.gtk_tree_view_column_set_visible (columnHandle, false); 613 } 614 OS.gtk_tree_view_column_set_resizable (columnHandle, true); 615 OS.gtk_tree_view_column_set_clickable (columnHandle, true); 616 OS.gtk_tree_view_column_set_min_width (columnHandle, 0); 617 OS.gtk_tree_view_insert_column (handle, columnHandle, index); 618 if (column !is null) { 619 column.handle = cast(GtkWidget*)columnHandle; 620 column.modelIndex = modelIndex; 621 } 622 /* Disable searching when using VIRTUAL */ 623 if ((style & SWT.VIRTUAL) !is 0) { 624 /* 625 * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE) 626 * would prevent the user from being able to type in text to search the tree. 627 * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive 628 * search. This meant that even if FALSE was passed to enable_search, the user 629 * can still bring up the search pop up using the keybinding. GTK also introduced 630 * the notion of passing a -1 to gtk_set_search_column to disable searching 631 * (including the search key binding). The fix is to use the right calls 632 * for the right version. 633 */ 634 if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) { 635 OS.gtk_tree_view_set_search_column (handle, -1); 636 } else { 637 OS.gtk_tree_view_set_enable_search (handle, false); 638 } 639 } else { 640 /* Set the search column whenever the model changes */ 641 int firstColumn = columnCount is 0 ? FIRST_COLUMN : columns [0].modelIndex; 642 OS.gtk_tree_view_set_search_column (handle, firstColumn + CELL_TEXT); 643 } 644 } 645 646 override void createHandle (int index) { 647 state |= HANDLE; 648 fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 649 if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES); 650 OS.gtk_fixed_set_has_window (fixedHandle, true); 651 scrolledHandle = cast(GtkWidget*)OS.gtk_scrolled_window_new (null, null); 652 if (scrolledHandle is null) error (SWT.ERROR_NO_HANDLES); 653 auto types = getColumnTypes (1); 654 modelHandle = cast(GtkWidget*)OS.gtk_list_store_newv 655 (cast(int)/*64bit*/types.length, types.ptr); 656 if (modelHandle is null) error (SWT.ERROR_NO_HANDLES); 657 handle = cast(GtkWidget*)OS.gtk_tree_view_new_with_model (modelHandle); 658 if (handle is null) error (SWT.ERROR_NO_HANDLES); 659 if ((style & SWT.CHECK) !is 0) { 660 checkRenderer = cast(GtkWidget*)OS.gtk_cell_renderer_toggle_new (); 661 if (checkRenderer is null) error (SWT.ERROR_NO_HANDLES); 662 OS.g_object_ref (checkRenderer); 663 } 664 createColumn (null, 0); 665 OS.gtk_container_add (fixedHandle, scrolledHandle); 666 OS.gtk_container_add (scrolledHandle, handle); 667 668 int mode = (style & SWT.MULTI) !is 0 ? OS.GTK_SELECTION_MULTIPLE : OS.GTK_SELECTION_BROWSE; 669 auto selectionHandle = OS.gtk_tree_view_get_selection (handle); 670 OS.gtk_tree_selection_set_mode (selectionHandle, mode); 671 OS.gtk_tree_view_set_headers_visible (handle, false); 672 int hsp = (style & SWT.H_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER; 673 int vsp = (style & SWT.V_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER; 674 OS.gtk_scrolled_window_set_policy (scrolledHandle, hsp, vsp); 675 if ((style & SWT.BORDER) !is 0) OS.gtk_scrolled_window_set_shadow_type (scrolledHandle, OS.GTK_SHADOW_ETCHED_IN); 676 /* Disable searching when using VIRTUAL */ 677 if ((style & SWT.VIRTUAL) !is 0) { 678 /* The fixed_height_mode property only exists in GTK 2.3.2 and greater */ 679 if (OS.GTK_VERSION >= OS.buildVERSION (2, 3, 2)) { 680 OS.g_object_set1 (handle, OS.fixed_height_mode.ptr, true); 681 } 682 /* 683 * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE) 684 * would prevent the user from being able to type in text to search the tree. 685 * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive 686 * search. This meant that even if FALSE was passed to enable_search, the user 687 * can still bring up the search pop up using the keybinding. GTK also introduced 688 * the notion of passing a -1 to gtk_set_search_column to disable searching 689 * (including the search key binding). The fix is to use the right calls 690 * for the right version. 691 */ 692 if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) { 693 OS.gtk_tree_view_set_search_column (handle, -1); 694 } else { 695 OS.gtk_tree_view_set_enable_search (handle, false); 696 } 697 } 698 } 699 700 void createItem (TableColumn column, int index) { 701 if (!(0 <= index && index <= columnCount)) error (SWT.ERROR_INVALID_RANGE); 702 if (columnCount is 0) { 703 column.handle = cast(GtkWidget*)OS.gtk_tree_view_get_column (handle, 0); 704 OS.gtk_tree_view_column_set_sizing (column.handle, OS.GTK_TREE_VIEW_COLUMN_FIXED); 705 OS.gtk_tree_view_column_set_visible (column.handle, false); 706 column.modelIndex = FIRST_COLUMN; 707 createRenderers (cast(GtkTreeViewColumn*)column.handle, column.modelIndex, true, column.style); 708 column.customDraw = firstCustomDraw; 709 firstCustomDraw = false; 710 } else { 711 createColumn (column, index); 712 } 713 auto boxHandle = OS.gtk_hbox_new (false, 3); 714 if (boxHandle is null) error (SWT.ERROR_NO_HANDLES); 715 auto labelHandle = OS.gtk_label_new_with_mnemonic (null); 716 if (labelHandle is null) error (SWT.ERROR_NO_HANDLES); 717 auto imageHandle = OS.gtk_image_new (); 718 if (imageHandle is null) error (SWT.ERROR_NO_HANDLES); 719 OS.gtk_container_add (boxHandle, imageHandle); 720 OS.gtk_container_add (boxHandle, labelHandle); 721 OS.gtk_widget_show (boxHandle); 722 OS.gtk_widget_show (labelHandle); 723 column.labelHandle = labelHandle; 724 column.imageHandle = imageHandle; 725 OS.gtk_tree_view_column_set_widget (column.handle, boxHandle); 726 auto widget = OS.gtk_widget_get_parent (boxHandle); 727 while (widget !is handle) { 728 if (OS.GTK_IS_BUTTON (cast(GTypeInstance*)widget)) { 729 column.buttonHandle = widget; 730 break; 731 } 732 widget = OS.gtk_widget_get_parent (widget); 733 } 734 if (columnCount is columns.length) { 735 TableColumn [] newColumns = new TableColumn [columns.length + 4]; 736 System.arraycopy (columns, 0, newColumns, 0, columns.length); 737 columns = newColumns; 738 } 739 System.arraycopy (columns, index, columns, index + 1, columnCount++ - index); 740 columns [index] = column; 741 if ((state & FONT) !is 0) { 742 column.setFontDescription (getFontDescription ()); 743 } 744 if (columnCount >= 1) { 745 for (int i=0; i<itemCount; i++) { 746 TableItem item = items [i]; 747 if (item !is null) { 748 Font [] cellFont = item.cellFont; 749 if (cellFont !is null) { 750 Font [] temp = new Font [columnCount]; 751 System.arraycopy (cellFont, 0, temp, 0, index); 752 System.arraycopy (cellFont, index, temp, index+1, columnCount-index-1); 753 item.cellFont = temp; 754 } 755 } 756 } 757 } 758 } 759 760 void createItem (TableItem item, int index) { 761 if (!(0 <= index && index <= itemCount)) error (SWT.ERROR_INVALID_RANGE); 762 if (itemCount == items.length) { 763 ptrdiff_t length = drawCount == 0 ? items.length + 4 764 : Math.max (4, items.length * 3 / 2); 765 TableItem [] newItems = new TableItem [length]; 766 System.arraycopy (items, 0, newItems, 0, items.length); 767 items = newItems; 768 } 769 item.handle = cast(GtkWidget*) OS.g_malloc( GtkTreeIter.sizeof ); 770 if (item.handle is null) error (SWT.ERROR_NO_HANDLES); 771 /* 772 * Feature in GTK. It is much faster to append to a list store 773 * than to insert at the end using gtk_list_store_insert(). 774 */ 775 if (index is itemCount) { 776 OS.gtk_list_store_append (modelHandle, item.handle); 777 } else { 778 OS.gtk_list_store_insert (modelHandle, item.handle, index); 779 } 780 System.arraycopy (items, index, items, index + 1, itemCount++ - index); 781 items [index] = item; 782 } 783 784 void createRenderers (GtkTreeViewColumn* columnHandle, int modelIndex, bool check, int columnStyle) { 785 OS.gtk_tree_view_column_clear (columnHandle); 786 if ((style & SWT.CHECK) !is 0 && check) { 787 OS.gtk_tree_view_column_pack_start (columnHandle, checkRenderer, false); 788 OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, OS.active.ptr, CHECKED_COLUMN); 789 /* 790 * Feature in GTK. The inconsistent property only exists in GTK 2.2.x. 791 */ 792 if (OS.GTK_VERSION >= OS.buildVERSION (2, 2, 0)) { 793 OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, OS.inconsistent.ptr, GRAYED_COLUMN); 794 } 795 /* 796 * Bug in GTK. GTK renders the background on top of the checkbox. 797 * This only happens in version 2.2.1 and earlier. The fix is not to set the background. 798 */ 799 if (OS.GTK_VERSION > OS.buildVERSION (2, 2, 1)) { 800 if (!ownerDraw) OS.gtk_tree_view_column_add_attribute (columnHandle, checkRenderer, OS.cell_background_gdk.ptr, BACKGROUND_COLUMN); 801 } 802 if (ownerDraw) { 803 display.doCellDataProc( handle, cast(GtkTreeViewColumn*)columnHandle, cast(GtkCellRenderer*)checkRenderer ); 804 OS.g_object_set_qdata (cast(GObject*)checkRenderer, Display.SWT_OBJECT_INDEX1, columnHandle); 805 } 806 } 807 auto pixbufRenderer = ownerDraw ? cast(GtkCellRenderer*)OS.g_object_new (display.gtk_cell_renderer_pixbuf_get_type (), null) : OS.gtk_cell_renderer_pixbuf_new (); 808 if (pixbufRenderer is null) error (SWT.ERROR_NO_HANDLES); 809 auto textRenderer = ownerDraw ? cast(GtkCellRenderer*)OS.g_object_new (display.gtk_cell_renderer_text_get_type (), null) : OS.gtk_cell_renderer_text_new (); 810 if (textRenderer is null) error (SWT.ERROR_NO_HANDLES); 811 812 if (ownerDraw) { 813 OS.g_object_set_qdata (cast(GObject*)pixbufRenderer, Display.SWT_OBJECT_INDEX1, columnHandle); 814 OS.g_object_set_qdata (cast(GObject*)textRenderer, Display.SWT_OBJECT_INDEX1, columnHandle); 815 } 816 817 /* 818 * Feature in GTK. When a tree view column contains only one activatable 819 * cell renderer such as a toggle renderer, mouse clicks anywhere in a cell 820 * activate that renderer. The workaround is to set a second cell renderer 821 * to be activatable. 822 */ 823 if ((style & SWT.CHECK) !is 0 && check) { 824 OS.g_object_set1 (pixbufRenderer, OS.mode.ptr, OS.GTK_CELL_RENDERER_MODE_ACTIVATABLE); 825 } 826 827 /* Set alignment */ 828 if ((columnStyle & SWT.RIGHT) !is 0) { 829 OS.g_object_set1_float(textRenderer, OS.xalign.ptr, 1.0f ); 830 OS.gtk_tree_view_column_pack_end (columnHandle, textRenderer, true); 831 OS.gtk_tree_view_column_pack_end (columnHandle, pixbufRenderer, false); 832 OS.gtk_tree_view_column_set_alignment (columnHandle, 1f); 833 } else if ((columnStyle & SWT.CENTER) !is 0) { 834 OS.g_object_set1_float(textRenderer, OS.xalign.ptr, 0.5f ); 835 OS.gtk_tree_view_column_pack_start (columnHandle, pixbufRenderer, false); 836 OS.gtk_tree_view_column_pack_end (columnHandle, textRenderer, true); 837 OS.gtk_tree_view_column_set_alignment (columnHandle, 0.5f); 838 } else { 839 OS.gtk_tree_view_column_pack_start (columnHandle, pixbufRenderer, false); 840 OS.gtk_tree_view_column_pack_start (columnHandle, textRenderer, true); 841 OS.gtk_tree_view_column_set_alignment (columnHandle, 0f); 842 } 843 844 /* Add attributes */ 845 OS.gtk_tree_view_column_add_attribute (columnHandle, pixbufRenderer, OS.pixbuf.ptr, modelIndex + CELL_PIXBUF); 846 /* 847 * Bug on GTK. Gtk renders the background on top of the pixbuf. 848 * This only happens in version 2.2.1 and earlier. The fix is not to set the background. 849 */ 850 if (OS.GTK_VERSION > OS.buildVERSION (2, 2, 1)) { 851 if (!ownerDraw) { 852 OS.gtk_tree_view_column_add_attribute (columnHandle, pixbufRenderer, OS.cell_background_gdk.ptr, BACKGROUND_COLUMN); 853 OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, OS.cell_background_gdk.ptr, BACKGROUND_COLUMN); 854 } 855 } 856 OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, OS.text.ptr, modelIndex + CELL_TEXT); 857 OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, OS.foreground_gdk.ptr, FOREGROUND_COLUMN); 858 OS.gtk_tree_view_column_add_attribute (columnHandle, textRenderer, OS.font_desc.ptr, FONT_COLUMN); 859 860 bool customDraw = firstCustomDraw; 861 if (columnCount !is 0) { 862 for (int i=0; i<columnCount; i++) { 863 if (columns [i].handle is cast(GtkWidget*)columnHandle) { 864 customDraw = columns [i].customDraw; 865 break; 866 } 867 } 868 } 869 if ((style & SWT.VIRTUAL) !is 0 || customDraw || ownerDraw) { 870 display.doCellDataProc( handle, cast(GtkTreeViewColumn*)columnHandle, cast(GtkCellRenderer*)textRenderer ); 871 display.doCellDataProc( handle, cast(GtkTreeViewColumn*)columnHandle, cast(GtkCellRenderer*)pixbufRenderer ); 872 } 873 } 874 875 override void createWidget (int index) { 876 super.createWidget (index); 877 items = new TableItem [4]; 878 columns = new TableColumn [4]; 879 itemCount = columnCount = 0; 880 } 881 882 GdkColor* defaultBackground () { 883 return display.COLOR_LIST_BACKGROUND; 884 } 885 886 GdkColor* defaultForeground () { 887 return display.COLOR_LIST_FOREGROUND; 888 } 889 890 override void deregister () { 891 super.deregister (); 892 display.removeWidget ( cast(GtkWidget*)OS.gtk_tree_view_get_selection (handle)); 893 if (checkRenderer !is null) display.removeWidget (checkRenderer); 894 } 895 896 /** 897 * Deselects the item at the given zero-relative index in the receiver. 898 * If the item at the index was already deselected, it remains 899 * deselected. Indices that are out of range are ignored. 900 * 901 * @param index the index of the item to deselect 902 * 903 * @exception SWTException <ul> 904 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 905 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 906 * </ul> 907 */ 908 public void deselect (int index) { 909 checkWidget(); 910 if (index < 0 || index >= itemCount) return; 911 bool fixColumn = showFirstColumn (); 912 auto selection = OS.gtk_tree_view_get_selection (handle); 913 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 914 OS.gtk_tree_selection_unselect_iter (selection, _getItem (index).handle); 915 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 916 if (fixColumn) hideFirstColumn (); 917 } 918 919 /** 920 * Deselects the items at the given zero-relative indices in the receiver. 921 * If the item at the given zero-relative index in the receiver 922 * is selected, it is deselected. If the item at the index 923 * was not selected, it remains deselected. The range of the 924 * indices is inclusive. Indices that are out of range are ignored. 925 * 926 * @param start the start index of the items to deselect 927 * @param end the end index of the items to deselect 928 * 929 * @exception SWTException <ul> 930 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 931 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 932 * </ul> 933 */ 934 public void deselect (int start, int end) { 935 checkWidget(); 936 bool fixColumn = showFirstColumn (); 937 auto selection = OS.gtk_tree_view_get_selection (handle); 938 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 939 for (int index=start; index<=end; index++) { 940 if (index < 0 || index >= itemCount) continue; 941 OS.gtk_tree_selection_unselect_iter (selection, _getItem (index).handle); 942 } 943 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 944 if (fixColumn) hideFirstColumn (); 945 } 946 947 /** 948 * Deselects the items at the given zero-relative indices in the receiver. 949 * If the item at the given zero-relative index in the receiver 950 * is selected, it is deselected. If the item at the index 951 * was not selected, it remains deselected. Indices that are out 952 * of range and duplicate indices are ignored. 953 * 954 * @param indices the array of indices for the items to deselect 955 * 956 * @exception SWTException <ul> 957 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 958 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 959 * </ul> 960 */ 961 public void deselect (int [] indices) { 962 checkWidget(); 963 // SWT extension: allow null for zero length string 964 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 965 bool fixColumn = showFirstColumn (); 966 auto selection = OS.gtk_tree_view_get_selection (handle); 967 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 968 for (int i=0; i<indices.length; i++) { 969 int index = indices[i]; 970 if (index < 0 || index >= itemCount) continue; 971 OS.gtk_tree_selection_unselect_iter (selection, _getItem (index).handle); 972 } 973 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 974 if (fixColumn) hideFirstColumn (); 975 } 976 977 /** 978 * Deselects all selected items in the receiver. 979 * 980 * @exception SWTException <ul> 981 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 982 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 983 * </ul> 984 */ 985 public void deselectAll () { 986 checkWidget(); 987 bool fixColumn = showFirstColumn (); 988 auto selection = OS.gtk_tree_view_get_selection (handle); 989 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 990 OS.gtk_tree_selection_unselect_all (selection); 991 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 992 if (fixColumn) hideFirstColumn (); 993 } 994 995 void destroyItem (TableColumn column) { 996 int index = 0; 997 while (index < columnCount) { 998 if (columns [index] is column) break; 999 index++; 1000 } 1001 if (index is columnCount) return; 1002 auto columnHandle = column.handle; 1003 if (columnCount is 1) { 1004 firstCustomDraw = column.customDraw; 1005 } 1006 System.arraycopy (columns, index + 1, columns, index, --columnCount - index); 1007 columns [columnCount] = null; 1008 OS.gtk_tree_view_remove_column (handle, columnHandle); 1009 if (columnCount is 0) { 1010 auto oldModel = modelHandle; 1011 auto types = getColumnTypes (1); 1012 auto newModel = OS.gtk_list_store_newv(cast(int)/*64bit*/types.length, types.ptr); 1013 if (newModel is null) error (SWT.ERROR_NO_HANDLES); 1014 void* ptr; 1015 for (int i=0; i<itemCount; i++) { 1016 GtkTreeIter* newItem = cast(GtkTreeIter*) OS.g_malloc( GtkTreeIter.sizeof ); 1017 if (newItem is null) error (SWT.ERROR_NO_HANDLES); 1018 OS.gtk_list_store_append (newModel, newItem); 1019 TableItem item = items [i]; 1020 if (item !is null) { 1021 auto oldItem = item.handle; 1022 for (int j=0; j<FIRST_COLUMN; j++) { 1023 OS.gtk_tree_model_get1 (oldModel, oldItem, j, &ptr); 1024 OS.gtk_list_store_set1 (newModel, newItem, j, ptr); 1025 } 1026 OS.gtk_tree_model_get1 (oldModel, oldItem, column.modelIndex + CELL_PIXBUF, &ptr); 1027 OS.gtk_list_store_set1 (newModel, newItem, FIRST_COLUMN + CELL_PIXBUF, ptr); 1028 OS.gtk_tree_model_get1 (oldModel, oldItem, column.modelIndex + CELL_TEXT, &ptr); 1029 OS.gtk_list_store_set1 (newModel, newItem, FIRST_COLUMN + CELL_TEXT, ptr ); 1030 OS.g_free (ptr); 1031 OS.gtk_tree_model_get1 (oldModel, oldItem, column.modelIndex + CELL_FOREGROUND, &ptr); 1032 OS.gtk_list_store_set1 (newModel, newItem, FIRST_COLUMN + CELL_FOREGROUND, ptr); 1033 OS.gtk_tree_model_get1 (oldModel, oldItem, column.modelIndex + CELL_BACKGROUND, &ptr); 1034 OS.gtk_list_store_set1 (newModel, newItem, FIRST_COLUMN + CELL_BACKGROUND, ptr); 1035 OS.gtk_tree_model_get1 (oldModel, oldItem, column.modelIndex + CELL_FONT, &ptr); 1036 OS.gtk_list_store_set1 (newModel, newItem, FIRST_COLUMN + CELL_FONT, ptr); 1037 OS.gtk_list_store_remove (oldModel, oldItem); 1038 OS.g_free (oldItem); 1039 item.handle = cast(GtkWidget*) newItem; 1040 } else { 1041 OS.g_free( newItem ); 1042 } 1043 } 1044 OS.gtk_tree_view_set_model (handle, newModel); 1045 OS.g_object_unref (oldModel); 1046 modelHandle = cast(GtkWidget*)newModel; 1047 createColumn (null, 0); 1048 } else { 1049 for (int i=0; i<itemCount; i++) { 1050 TableItem item = items [i]; 1051 if (item !is null) { 1052 auto iter = item.handle; 1053 int modelIndex = column.modelIndex; 1054 OS.gtk_list_store_set1 (modelHandle, iter, modelIndex + CELL_PIXBUF, null); 1055 OS.gtk_list_store_set1 (modelHandle, iter, modelIndex + CELL_TEXT, null); 1056 OS.gtk_list_store_set1 (modelHandle, iter, modelIndex + CELL_FOREGROUND, null); 1057 OS.gtk_list_store_set1 (modelHandle, iter, modelIndex + CELL_BACKGROUND, null); 1058 OS.gtk_list_store_set1 (modelHandle, iter, modelIndex + CELL_FONT, null); 1059 1060 Font [] cellFont = item.cellFont; 1061 if (cellFont !is null) { 1062 if (columnCount is 0) { 1063 item.cellFont = null; 1064 } else { 1065 Font [] temp = new Font [columnCount]; 1066 System.arraycopy (cellFont, 0, temp, 0, index); 1067 System.arraycopy (cellFont, index + 1, temp, index, columnCount - index); 1068 item.cellFont = temp; 1069 } 1070 } 1071 } 1072 } 1073 if (index is 0) { 1074 TableColumn checkColumn = columns [0]; 1075 createRenderers (cast(GtkTreeViewColumn*)checkColumn.handle, checkColumn.modelIndex, true, checkColumn.style); 1076 } 1077 } 1078 /* Disable searching when using VIRTUAL */ 1079 if ((style & SWT.VIRTUAL) !is 0) { 1080 /* 1081 * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE) 1082 * would prevent the user from being able to type in text to search the tree. 1083 * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive 1084 * search. This meant that even if FALSE was passed to enable_search, the user 1085 * can still bring up the search pop up using the keybinding. GTK also introduced 1086 * the notion of passing a -1 to gtk_set_search_column to disable searching 1087 * (including the search key binding). The fix is to use the right calls 1088 * for the right version. 1089 */ 1090 if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) { 1091 OS.gtk_tree_view_set_search_column (handle, -1); 1092 } else { 1093 OS.gtk_tree_view_set_enable_search (handle, false); 1094 } 1095 } else { 1096 /* Set the search column whenever the model changes */ 1097 int firstColumn = columnCount is 0 ? FIRST_COLUMN : columns [0].modelIndex; 1098 OS.gtk_tree_view_set_search_column (handle, firstColumn + CELL_TEXT); 1099 } 1100 } 1101 1102 void destroyItem (TableItem item) { 1103 int index = 0; 1104 while (index < itemCount) { 1105 if (items [index] is item) break; 1106 index++; 1107 } 1108 if (index is itemCount) return; 1109 auto selection = OS.gtk_tree_view_get_selection (handle); 1110 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1111 OS.gtk_list_store_remove (modelHandle, item.handle); 1112 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1113 System.arraycopy (items, index + 1, items, index, --itemCount - index); 1114 items [itemCount] = null; 1115 if (itemCount is 0) resetCustomDraw (); 1116 } 1117 1118 override bool dragDetect (int x, int y, bool filter, bool* consume) { 1119 bool selected = false; 1120 if (filter) { 1121 GtkTreeViewColumn * path; 1122 if (OS.gtk_tree_view_get_path_at_pos (handle, x, y, cast(void**)&path, null, null, null)) { 1123 if (path !is null) { 1124 auto selection = OS.gtk_tree_view_get_selection (handle); 1125 if (OS.gtk_tree_selection_path_is_selected (selection, path)) selected = true; 1126 OS.gtk_tree_path_free (path); 1127 } 1128 } else { 1129 return false; 1130 } 1131 } 1132 bool dragDetect = super.dragDetect (x, y, filter, consume); 1133 if (dragDetect && selected && consume !is null) consume[0] = true; 1134 return dragDetect; 1135 } 1136 1137 override GdkDrawable* eventWindow () { 1138 return paintWindow (); 1139 } 1140 1141 override void fixChildren (Shell newShell, Shell oldShell, Decorations newDecorations, Decorations oldDecorations, Menu [] menus) { 1142 super.fixChildren (newShell, oldShell, newDecorations, oldDecorations, menus); 1143 for (int i=0; i<columnCount; i++) { 1144 TableColumn column = columns [i]; 1145 if (column.toolTipText !is null) { 1146 column.setToolTipText(oldShell, null); 1147 column.setToolTipText(newShell, column.toolTipText); 1148 } 1149 } 1150 } 1151 1152 override GdkColor* getBackgroundColor () { 1153 return getBaseColor (); 1154 } 1155 1156 public override Rectangle getClientArea () { 1157 checkWidget (); 1158 forceResize (); 1159 OS.gtk_widget_realize (handle); 1160 auto fixedWindow = OS.GTK_WIDGET_WINDOW (fixedHandle); 1161 auto binWindow = OS.gtk_tree_view_get_bin_window (handle); 1162 int binX, binY; 1163 OS.gdk_window_get_origin (binWindow, &binX, &binY); 1164 int fixedX, fixedY; 1165 OS.gdk_window_get_origin (fixedWindow, &fixedX, &fixedY); 1166 auto clientHandle = clientHandle (); 1167 int width = (state & ZERO_WIDTH) !is 0 ? 0 : OS.GTK_WIDGET_WIDTH (clientHandle); 1168 int height = (state & ZERO_HEIGHT) !is 0 ? 0 : OS.GTK_WIDGET_HEIGHT (clientHandle); 1169 return new Rectangle (fixedX - binX, fixedY - binY, width, height); 1170 } 1171 1172 override 1173 int getClientWidth () { 1174 int w, h; 1175 OS.gtk_widget_realize (handle); 1176 OS.gdk_drawable_get_size(OS.gtk_tree_view_get_bin_window(handle), &w, &h); 1177 return w; 1178 } 1179 1180 /** 1181 * Returns the column at the given, zero-relative index in the 1182 * receiver. Throws an exception if the index is out of range. 1183 * Columns are returned in the order that they were created. 1184 * If no <code>TableColumn</code>s were created by the programmer, 1185 * this method will throw <code>ERROR_INVALID_RANGE</code> despite 1186 * the fact that a single column of data may be visible in the table. 1187 * This occurs when the programmer uses the table like a list, adding 1188 * items but never creating a column. 1189 * 1190 * @param index the index of the column to return 1191 * @return the column at the given index 1192 * 1193 * @exception IllegalArgumentException <ul> 1194 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 1195 * </ul> 1196 * @exception SWTException <ul> 1197 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1198 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1199 * </ul> 1200 * 1201 * @see Table#getColumnOrder() 1202 * @see Table#setColumnOrder(int[]) 1203 * @see TableColumn#getMoveable() 1204 * @see TableColumn#setMoveable(bool) 1205 * @see SWT#Move 1206 */ 1207 public TableColumn getColumn (int index) { 1208 checkWidget(); 1209 if (!(0 <= index && index < columnCount)) error (SWT.ERROR_INVALID_RANGE); 1210 return columns [index]; 1211 } 1212 1213 /** 1214 * Returns the number of columns contained in the receiver. 1215 * If no <code>TableColumn</code>s were created by the programmer, 1216 * this value is zero, despite the fact that visually, one column 1217 * of items may be visible. This occurs when the programmer uses 1218 * the table like a list, adding items but never creating a column. 1219 * 1220 * @return the number of columns 1221 * 1222 * @exception SWTException <ul> 1223 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1224 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1225 * </ul> 1226 */ 1227 public int getColumnCount () { 1228 checkWidget(); 1229 return columnCount; 1230 } 1231 1232 size_t[] getColumnTypes (int columnCount) { 1233 size_t[] types = new size_t [FIRST_COLUMN + (columnCount * CELL_TYPES)]; 1234 // per row data 1235 types [CHECKED_COLUMN] = OS.G_TYPE_BOOLEAN (); 1236 types [GRAYED_COLUMN] = OS.G_TYPE_BOOLEAN (); 1237 types [FOREGROUND_COLUMN] = OS.GDK_TYPE_COLOR (); 1238 types [BACKGROUND_COLUMN] = OS.GDK_TYPE_COLOR (); 1239 types [FONT_COLUMN] = OS.PANGO_TYPE_FONT_DESCRIPTION (); 1240 // per cell data 1241 for (int i=FIRST_COLUMN; i<types.length; i+=CELL_TYPES) { 1242 types [i + CELL_PIXBUF] = OS.GDK_TYPE_PIXBUF (); 1243 types [i + CELL_TEXT] = OS.G_TYPE_STRING (); 1244 types [i + CELL_FOREGROUND] = OS.GDK_TYPE_COLOR (); 1245 types [i + CELL_BACKGROUND] = OS.GDK_TYPE_COLOR (); 1246 types [i + CELL_FONT] = OS.PANGO_TYPE_FONT_DESCRIPTION (); 1247 } 1248 return types; 1249 } 1250 1251 /** 1252 * Returns an array of zero-relative integers that map 1253 * the creation order of the receiver's items to the 1254 * order in which they are currently being displayed. 1255 * <p> 1256 * Specifically, the indices of the returned array represent 1257 * the current visual order of the items, and the contents 1258 * of the array represent the creation order of the items. 1259 * </p><p> 1260 * Note: This is not the actual structure used by the receiver 1261 * to maintain its list of items, so modifying the array will 1262 * not affect the receiver. 1263 * </p> 1264 * 1265 * @return the current visual order of the receiver's items 1266 * 1267 * @exception SWTException <ul> 1268 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1269 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1270 * </ul> 1271 * 1272 * @see Table#setColumnOrder(int[]) 1273 * @see TableColumn#getMoveable() 1274 * @see TableColumn#setMoveable(bool) 1275 * @see SWT#Move 1276 * 1277 * @since 3.1 1278 */ 1279 public int [] getColumnOrder () { 1280 checkWidget (); 1281 if (columnCount is 0) return new int [0]; 1282 auto list = OS.gtk_tree_view_get_columns (handle); 1283 if (list is null) return new int [0]; 1284 int i = 0, count = OS.g_list_length (list); 1285 int [] order = new int [count]; 1286 auto temp = list; 1287 while (temp !is null) { 1288 auto column = OS.g_list_data (temp); 1289 if (column !is null) { 1290 for (int j=0; j<columnCount; j++) { 1291 if (columns [j].handle is column) { 1292 order [i++] = j; 1293 break; 1294 } 1295 } 1296 } 1297 temp = OS.g_list_next (temp); 1298 } 1299 OS.g_list_free (list); 1300 return order; 1301 } 1302 1303 /** 1304 * Returns an array of <code>TableColumn</code>s which are the 1305 * columns in the receiver. Columns are returned in the order 1306 * that they were created. If no <code>TableColumn</code>s were 1307 * created by the programmer, the array is empty, despite the fact 1308 * that visually, one column of items may be visible. This occurs 1309 * when the programmer uses the table like a list, adding items but 1310 * never creating a column. 1311 * <p> 1312 * Note: This is not the actual structure used by the receiver 1313 * to maintain its list of items, so modifying the array will 1314 * not affect the receiver. 1315 * </p> 1316 * 1317 * @return the items in the receiver 1318 * 1319 * @exception SWTException <ul> 1320 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1321 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1322 * </ul> 1323 * 1324 * @see Table#getColumnOrder() 1325 * @see Table#setColumnOrder(int[]) 1326 * @see TableColumn#getMoveable() 1327 * @see TableColumn#setMoveable(bool) 1328 * @see SWT#Move 1329 */ 1330 public TableColumn [] getColumns () { 1331 checkWidget(); 1332 TableColumn [] result = new TableColumn [columnCount]; 1333 System.arraycopy (columns, 0, result, 0, columnCount); 1334 return result; 1335 } 1336 1337 TableItem getFocusItem () { 1338 GtkTreeViewColumn * path; 1339 OS.gtk_tree_view_get_cursor (handle, cast(void**)&path, null); 1340 if (path is null) return null; 1341 TableItem item = null; 1342 auto indices = OS.gtk_tree_path_get_indices (path); 1343 if (indices !is null) { 1344 item = _getItem (indices [0]); 1345 } 1346 OS.gtk_tree_path_free (path); 1347 return item; 1348 } 1349 1350 override GdkColor* getForegroundColor () { 1351 return getTextColor (); 1352 } 1353 1354 /** 1355 * Returns the width in pixels of a grid line. 1356 * 1357 * @return the width of a grid line in pixels 1358 * 1359 * @exception SWTException <ul> 1360 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1361 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1362 * </ul> 1363 */ 1364 public int getGridLineWidth () { 1365 checkWidget(); 1366 return 0; 1367 } 1368 1369 /** 1370 * Returns the height of the receiver's header 1371 * 1372 * @return the height of the header or zero if the header is not visible 1373 * 1374 * @exception SWTException <ul> 1375 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1376 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1377 * </ul> 1378 * 1379 * @since 2.0 1380 */ 1381 public int getHeaderHeight () { 1382 checkWidget (); 1383 if (!OS.gtk_tree_view_get_headers_visible (handle)) return 0; 1384 if (columnCount > 0) { 1385 GtkRequisition requisition; 1386 int height = 0; 1387 for (int i=0; i<columnCount; i++) { 1388 auto buttonHandle = columns [i].buttonHandle; 1389 if (buttonHandle !is null) { 1390 OS.gtk_widget_size_request (buttonHandle, &requisition); 1391 height = Math.max (height, requisition.height); 1392 } 1393 } 1394 return height; 1395 } 1396 OS.gtk_widget_realize (handle); 1397 auto fixedWindow = OS.GTK_WIDGET_WINDOW (fixedHandle); 1398 auto binWindow = OS.gtk_tree_view_get_bin_window (handle); 1399 int binY; 1400 OS.gdk_window_get_origin (binWindow, null, &binY); 1401 int fixedY; 1402 OS.gdk_window_get_origin (fixedWindow, null, &fixedY); 1403 return binY - fixedY; 1404 } 1405 1406 /** 1407 * Returns <code>true</code> if the receiver's header is visible, 1408 * and <code>false</code> otherwise. 1409 * <p> 1410 * If one of the receiver's ancestors is not visible or some 1411 * other condition makes the receiver not visible, this method 1412 * may still indicate that it is considered visible even though 1413 * it may not actually be showing. 1414 * </p> 1415 * 1416 * @return the receiver's header's visibility state 1417 * 1418 * @exception SWTException <ul> 1419 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1420 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1421 * </ul> 1422 */ 1423 public bool getHeaderVisible () { 1424 checkWidget(); 1425 return cast(bool) OS.gtk_tree_view_get_headers_visible (handle); 1426 } 1427 1428 /** 1429 * Returns the item at the given, zero-relative index in the 1430 * receiver. Throws an exception if the index is out of range. 1431 * 1432 * @param index the index of the item to return 1433 * @return the item at the given index 1434 * 1435 * @exception IllegalArgumentException <ul> 1436 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 1437 * </ul> 1438 * @exception SWTException <ul> 1439 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1440 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1441 * </ul> 1442 */ 1443 public TableItem getItem (int index) { 1444 checkWidget(); 1445 if (!(0 <= index && index < itemCount)) error (SWT.ERROR_INVALID_RANGE); 1446 return _getItem (index); 1447 } 1448 1449 /** 1450 * Returns the item at the given point in the receiver 1451 * or null if no such item exists. The point is in the 1452 * coordinate system of the receiver. 1453 * <p> 1454 * The item that is returned represents an item that could be selected by the user. 1455 * For example, if selection only occurs in items in the first column, then null is 1456 * returned if the point is outside of the item. 1457 * Note that the SWT.FULL_SELECTION style hint, which specifies the selection policy, 1458 * determines the extent of the selection. 1459 * </p> 1460 * 1461 * @param point the point used to locate the item 1462 * @return the item at the given point, or null if the point is not in a selectable item 1463 * 1464 * @exception IllegalArgumentException <ul> 1465 * <li>ERROR_NULL_ARGUMENT - if the point is null</li> 1466 * </ul> 1467 * @exception SWTException <ul> 1468 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1469 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1470 * </ul> 1471 */ 1472 public TableItem getItem (Point point) { 1473 checkWidget(); 1474 if (point is null) error (SWT.ERROR_NULL_ARGUMENT); 1475 void* path; 1476 OS.gtk_widget_realize (handle); 1477 if (!OS.gtk_tree_view_get_path_at_pos (handle, point.x, point.y, &path, null, null, null)) return null; 1478 if (path is null) return null; 1479 auto indices = OS.gtk_tree_path_get_indices (path ); 1480 TableItem item = null; 1481 if (indices !is null) { 1482 item = _getItem (indices [0]); 1483 } 1484 OS.gtk_tree_path_free (path ); 1485 return item; 1486 } 1487 1488 /** 1489 * Returns the number of items contained in the receiver. 1490 * 1491 * @return the number of items 1492 * 1493 * @exception SWTException <ul> 1494 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1495 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1496 * </ul> 1497 */ 1498 public int getItemCount () { 1499 checkWidget (); 1500 return itemCount; 1501 } 1502 1503 /** 1504 * Returns the height of the area which would be used to 1505 * display <em>one</em> of the items in the receiver. 1506 * 1507 * @return the height of one item 1508 * 1509 * @exception SWTException <ul> 1510 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1511 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1512 * </ul> 1513 */ 1514 public int getItemHeight () { 1515 checkWidget(); 1516 if (itemCount is 0) { 1517 auto column = OS.gtk_tree_view_get_column (handle, 0); 1518 int w, h; 1519 ignoreSize = true; 1520 OS.gtk_tree_view_column_cell_get_size (column, null, null, null, &w, &h); 1521 ignoreSize = false; 1522 return h; 1523 } else { 1524 int height = 0; 1525 GtkTreeIter* iter = cast(GtkTreeIter*) OS.g_malloc( GtkTreeIter.sizeof ); 1526 OS.gtk_tree_model_get_iter_first (modelHandle, iter); 1527 int columnCount = Math.max (1, this.columnCount); 1528 for (int i=0; i<columnCount; i++) { 1529 auto column = OS.gtk_tree_view_get_column (handle, i); 1530 OS.gtk_tree_view_column_cell_set_cell_data (column, modelHandle, iter, false, false); 1531 int w, h; 1532 OS.gtk_tree_view_column_cell_get_size (column, null, null, null, &w, &h); 1533 height = Math.max (height, h ); 1534 } 1535 OS.g_free(iter); 1536 return height; 1537 } 1538 } 1539 1540 /** 1541 * Returns a (possibly empty) array of <code>TableItem</code>s which 1542 * are the items in the receiver. 1543 * <p> 1544 * Note: This is not the actual structure used by the receiver 1545 * to maintain its list of items, so modifying the array will 1546 * not affect the receiver. 1547 * </p> 1548 * 1549 * @return the items in the receiver 1550 * 1551 * @exception SWTException <ul> 1552 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1553 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1554 * </ul> 1555 */ 1556 public TableItem [] getItems () { 1557 checkWidget(); 1558 TableItem [] result = new TableItem [itemCount]; 1559 if ((style & SWT.VIRTUAL) !is 0) { 1560 for (int i=0; i<itemCount; i++) { 1561 result [i] = _getItem (i); 1562 } 1563 } else { 1564 System.arraycopy (items, 0, result, 0, itemCount); 1565 } 1566 return result; 1567 } 1568 1569 /** 1570 * Returns <code>true</code> if the receiver's lines are visible, 1571 * and <code>false</code> otherwise. 1572 * <p> 1573 * If one of the receiver's ancestors is not visible or some 1574 * other condition makes the receiver not visible, this method 1575 * may still indicate that it is considered visible even though 1576 * it may not actually be showing. 1577 * </p> 1578 * 1579 * @return the visibility state of the lines 1580 * 1581 * @exception SWTException <ul> 1582 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1583 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1584 * </ul> 1585 */ 1586 public bool getLinesVisible() { 1587 checkWidget(); 1588 return cast(bool)OS.gtk_tree_view_get_rules_hint (handle); 1589 } 1590 1591 GtkCellRendererPixbuf* getPixbufRenderer (GtkTreeViewColumn* column) { 1592 auto list = OS.gtk_tree_view_column_get_cell_renderers (column); 1593 if (list is null) return null; 1594 int count = OS.g_list_length (list); 1595 GtkCellRendererPixbuf* pixbufRenderer; 1596 int i = 0; 1597 while (i < count) { 1598 auto renderer = OS.g_list_nth_data (list, i); 1599 if (OS.GTK_IS_CELL_RENDERER_PIXBUF (cast(GTypeInstance*)renderer)) { 1600 pixbufRenderer = cast(GtkCellRendererPixbuf*)renderer; 1601 break; 1602 } 1603 i++; 1604 } 1605 OS.g_list_free (list); 1606 return pixbufRenderer; 1607 } 1608 1609 /** 1610 * Returns an array of <code>TableItem</code>s that are currently 1611 * selected in the receiver. The order of the items is unspecified. 1612 * An empty array indicates that no items are selected. 1613 * <p> 1614 * Note: This is not the actual structure used by the receiver 1615 * to maintain its selection, so modifying the array will 1616 * not affect the receiver. 1617 * </p> 1618 * @return an array representing the selection 1619 * 1620 * @exception SWTException <ul> 1621 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1622 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1623 * </ul> 1624 */ 1625 public TableItem [] getSelection () { 1626 checkWidget(); 1627 auto selection = OS.gtk_tree_view_get_selection (handle); 1628 if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) { 1629 display.treeSelectionLength = 0; 1630 display.treeSelection = new int [itemCount]; 1631 display.doTreeSelectionProcConnect( &treeSelectionCallbackData, handle, selection ); 1632 TableItem [] result = new TableItem [display.treeSelectionLength]; 1633 for (int i=0; i<result.length; i++) result [i] = _getItem (display.treeSelection [i]); 1634 return result; 1635 } 1636 /* 1637 * Bug in GTK. gtk_tree_selection_get_selected_rows() segmentation faults 1638 * in versions smaller than 2.2.4 if the model is NULL. The fix is 1639 * to give a valid pointer instead. 1640 */ 1641 int dummy; 1642 void* model = OS.GTK_VERSION < OS.buildVERSION (2, 2, 4) ? &dummy : null; 1643 auto list = OS.gtk_tree_selection_get_selected_rows (selection, &model); 1644 if (list !is null) { 1645 int count = OS.g_list_length (list); 1646 int [] treeSelection = new int [count]; 1647 int length_ = 0; 1648 for (int i=0; i<count; i++) { 1649 auto data = OS.g_list_nth_data (list, i); 1650 auto indices = OS.gtk_tree_path_get_indices (data); 1651 if (indices !is null) { 1652 treeSelection [length_] = indices [0]; 1653 length_++; 1654 } 1655 } 1656 OS.g_list_free (list); 1657 TableItem [] result = new TableItem [length_]; 1658 for (int i=0; i<result.length; i++) result [i] = _getItem (treeSelection [i]); 1659 return result; 1660 } 1661 return null; 1662 } 1663 1664 /** 1665 * Returns the number of selected items contained in the receiver. 1666 * 1667 * @return the number of selected items 1668 * 1669 * @exception SWTException <ul> 1670 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1671 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1672 * </ul> 1673 */ 1674 public int getSelectionCount () { 1675 checkWidget(); 1676 auto selection = OS.gtk_tree_view_get_selection (handle); 1677 if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) { 1678 display.treeSelectionLength = 0; 1679 display.treeSelection = null; 1680 display.doTreeSelectionProcConnect( &treeSelectionCallbackData, handle, selection ); 1681 return display.treeSelectionLength; 1682 } 1683 return OS.gtk_tree_selection_count_selected_rows (selection); 1684 } 1685 1686 /** 1687 * Returns the zero-relative index of the item which is currently 1688 * selected in the receiver, or -1 if no item is selected. 1689 * 1690 * @return the index of the selected item 1691 * 1692 * @exception SWTException <ul> 1693 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1694 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1695 * </ul> 1696 */ 1697 public int getSelectionIndex () { 1698 checkWidget(); 1699 if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) { 1700 display.treeSelectionLength = 0; 1701 display.treeSelection = new int [itemCount]; 1702 auto selection = OS.gtk_tree_view_get_selection (handle); 1703 display.doTreeSelectionProcConnect( &treeSelectionCallbackData, handle, selection ); 1704 if (display.treeSelectionLength is 0) return -1; 1705 return display.treeSelection [0]; 1706 } 1707 auto selection = OS.gtk_tree_view_get_selection (handle); 1708 /* 1709 * Bug in GTK. gtk_tree_selection_get_selected_rows() segmentation faults 1710 * in versions smaller than 2.2.4 if the model is NULL. The fix is 1711 * to give a valid pointer instead. 1712 */ 1713 int dummy; 1714 void* model = OS.GTK_VERSION < OS.buildVERSION (2, 2, 4) ? &dummy : null; 1715 auto list = OS.gtk_tree_selection_get_selected_rows (selection, &model); 1716 if (list !is null) { 1717 int count = OS.g_list_length (list); 1718 int index; 1719 for (int i=0; i<count; i++) { 1720 auto data = OS.g_list_nth_data (list, i); 1721 auto indices = OS.gtk_tree_path_get_indices (data); 1722 if (indices !is null) { 1723 index = indices[0]; 1724 break; 1725 } 1726 } 1727 OS.g_list_free (list); 1728 return index; 1729 } 1730 return -1; 1731 } 1732 1733 /** 1734 * Returns the zero-relative indices of the items which are currently 1735 * selected in the receiver. The order of the indices is unspecified. 1736 * The array is empty if no items are selected. 1737 * <p> 1738 * Note: This is not the actual structure used by the receiver 1739 * to maintain its selection, so modifying the array will 1740 * not affect the receiver. 1741 * </p> 1742 * @return the array of indices of the selected items 1743 * 1744 * @exception SWTException <ul> 1745 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1746 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1747 * </ul> 1748 */ 1749 public int [] getSelectionIndices () { 1750 checkWidget(); 1751 auto selection = OS.gtk_tree_view_get_selection (handle); 1752 if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) { 1753 display.treeSelectionLength = 0; 1754 display.treeSelection = new int [itemCount]; 1755 display.doTreeSelectionProcConnect( &treeSelectionCallbackData, handle, selection ); 1756 int [] result = new int [display.treeSelectionLength]; 1757 for (int i=0; i<display.treeSelectionLength; i++) { 1758 result[i] = display.treeSelection[i]; 1759 } 1760 return result; 1761 } 1762 /* 1763 * Bug in GTK. gtk_tree_selection_get_selected_rows() segmentation faults 1764 * in versions smaller than 2.2.4 if the model is NULL. The fix is 1765 * to give a valid pointer instead. 1766 */ 1767 int dummy; 1768 void* model = OS.GTK_VERSION < OS.buildVERSION (2, 2, 4) ? &dummy : null; 1769 auto list = OS.gtk_tree_selection_get_selected_rows (selection, &model); 1770 if (list !is null) { 1771 int count = OS.g_list_length (list); 1772 int [] treeSelection = new int [count]; 1773 int length_ = 0; 1774 for (int i=0; i<count; i++) { 1775 auto data = OS.g_list_nth_data (list, i); 1776 auto indices = OS.gtk_tree_path_get_indices (data); 1777 if (indices !is null) { 1778 treeSelection [length_] = indices [0]; 1779 length_++; 1780 } 1781 } 1782 OS.g_list_free (list); 1783 int [] result = new int [length_]; 1784 System.arraycopy (treeSelection, 0, result, 0, length_); 1785 return result; 1786 } 1787 return null; 1788 } 1789 1790 /** 1791 * Returns the column which shows the sort indicator for 1792 * the receiver. The value may be null if no column shows 1793 * the sort indicator. 1794 * 1795 * @return the sort indicator 1796 * 1797 * @exception SWTException <ul> 1798 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1799 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1800 * </ul> 1801 * 1802 * @see #setSortColumn(TableColumn) 1803 * 1804 * @since 3.2 1805 */ 1806 public TableColumn getSortColumn () { 1807 checkWidget (); 1808 return sortColumn; 1809 } 1810 1811 /** 1812 * Returns the direction of the sort indicator for the receiver. 1813 * The value will be one of <code>UP</code>, <code>DOWN</code> 1814 * or <code>NONE</code>. 1815 * 1816 * @return the sort direction 1817 * 1818 * @exception SWTException <ul> 1819 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1820 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1821 * </ul> 1822 * 1823 * @see #setSortDirectioncast(int) 1824 * 1825 * @since 3.2 1826 */ 1827 public int getSortDirection () { 1828 checkWidget (); 1829 return sortDirection; 1830 } 1831 1832 GtkCellRendererText* getTextRenderer (GtkTreeViewColumn* column) { 1833 auto list = OS.gtk_tree_view_column_get_cell_renderers (column); 1834 if (list is null) return null; 1835 int count = OS.g_list_length (list); 1836 GtkCellRendererText* textRenderer; 1837 int i = 0; 1838 while (i < count) { 1839 auto renderer = OS.g_list_nth_data (list, i); 1840 if (OS.GTK_IS_CELL_RENDERER_TEXT (cast(GTypeInstance*)renderer)) { 1841 textRenderer = cast(GtkCellRendererText*)renderer; 1842 break; 1843 } 1844 i++; 1845 } 1846 OS.g_list_free (list); 1847 return textRenderer; 1848 } 1849 1850 /** 1851 * Returns the zero-relative index of the item which is currently 1852 * at the top of the receiver. This index can change when items are 1853 * scrolled or new items are added or removed. 1854 * 1855 * @return the index of the top item 1856 * 1857 * @exception SWTException <ul> 1858 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1859 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1860 * </ul> 1861 */ 1862 public int getTopIndex () { 1863 checkWidget(); 1864 void* path; 1865 OS.gtk_widget_realize (handle); 1866 if (!OS.gtk_tree_view_get_path_at_pos (handle, 1, 1, &path, null, null, null)) return 0; 1867 if (path is null) return 0; 1868 auto indices = OS.gtk_tree_path_get_indices (path); 1869 int index; 1870 if (indices !is null) index = indices[0]; 1871 OS.gtk_tree_path_free (path); 1872 return index; 1873 } 1874 1875 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 1876 if (gdkEvent.window !is OS.gtk_tree_view_get_bin_window (handle)) return 0; 1877 auto result = super.gtk_button_press_event (widget, gdkEvent); 1878 if (result !is 0) return result; 1879 /* 1880 * Feature in GTK. In a multi-select table view, when multiple items are already 1881 * selected, the selection state of the item is toggled and the previous selection 1882 * is cleared. This is not the desired behaviour when bringing up a popup menu. 1883 * Also, when an item is reselected with the right button, the tree view issues 1884 * an unwanted selection event. The workaround is to detect that case and not 1885 * run the default handler when the item is already part of the current selection. 1886 */ 1887 int button = gdkEvent.button; 1888 if (button is 3 && gdkEvent.type is OS.GDK_BUTTON_PRESS) { 1889 void* path; 1890 if (OS.gtk_tree_view_get_path_at_pos (handle, cast(int)gdkEvent.x, cast(int)gdkEvent.y, &path, null, null, null)) { 1891 if (path !is null) { 1892 auto selection = OS.gtk_tree_view_get_selection (handle); 1893 if (OS.gtk_tree_selection_path_is_selected (selection, path)) result = 1; 1894 OS.gtk_tree_path_free (path); 1895 } 1896 } 1897 } 1898 1899 /* 1900 * Feature in GTK. When the user clicks in a single selection GtkTreeView 1901 * and there are no selected items, the first item is selected automatically 1902 * before the click is processed, causing two selection events. The is fix 1903 * is the set the cursor item to be same as the clicked item to stop the 1904 * widget from automatically selecting the first item. 1905 */ 1906 if ((style & SWT.SINGLE) !is 0 && getSelectionCount () is 0) { 1907 void* path; 1908 if (OS.gtk_tree_view_get_path_at_pos (handle, cast(int)gdkEvent.x, cast(int)gdkEvent.y, &path, null, null, null)) { 1909 if (path !is null) { 1910 auto selection = OS.gtk_tree_view_get_selection (handle); 1911 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1912 OS.gtk_tree_view_set_cursor (handle, path, null, false); 1913 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1914 OS.gtk_tree_path_free (path); 1915 } 1916 } 1917 } 1918 /* 1919 * Bug in GTK. GTK segments fault, if the GtkTreeView widget is 1920 * not in focus and all items in the widget are disposed before 1921 * it finishes processing a button press. The fix is to give 1922 * focus to the widget before it starts processing the event. 1923 */ 1924 if (!OS.GTK_WIDGET_HAS_FOCUS (handle)) { 1925 OS.gtk_widget_grab_focus (handle); 1926 } 1927 return result; 1928 } 1929 1930 override int gtk_button_release_event (GtkWidget* widget, GdkEventButton* event) { 1931 auto window = OS.GDK_EVENT_WINDOW (event); 1932 if (window !is OS.gtk_tree_view_get_bin_window (handle)) return 0; 1933 return super.gtk_button_release_event (widget, event); 1934 } 1935 1936 override int gtk_changed (GtkWidget* widget) { 1937 TableItem item = getFocusItem (); 1938 if (item !is null) { 1939 Event event = new Event (); 1940 event.item = item; 1941 postEvent (SWT.Selection, event); 1942 } 1943 return 0; 1944 } 1945 1946 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* keyEvent) { 1947 auto result = super.gtk_key_press_event (widget, keyEvent); 1948 if (result !is 0) return result; 1949 if (OS.GTK_VERSION < OS.buildVERSION (2, 2 ,0)) { 1950 /* 1951 * Feature in GTK 2.0.x. When an item is default selected using 1952 * the return key, GTK does not issue notification. The fix is 1953 * to issue this notification when the return key is pressed. 1954 */ 1955 int key = keyEvent.keyval; 1956 switch (key) { 1957 case OS.GDK_Return: 1958 case OS.GDK_KP_Enter: { 1959 Event event = new Event (); 1960 event.item = getFocusItem (); 1961 postEvent (SWT.DefaultSelection, event); 1962 break; 1963 } 1964 default: 1965 } 1966 } 1967 return result; 1968 } 1969 1970 override int gtk_popup_menu (GtkWidget* widget) { 1971 auto result = super.gtk_popup_menu (widget); 1972 /* 1973 * Bug in GTK. The context menu for the typeahead in GtkTreeViewer 1974 * opens in the bottom right corner of the screen when Shift+F10 1975 * is pressed and the typeahead window was not visible. The fix is 1976 * to prevent the context menu from opening by stopping the default 1977 * handler. 1978 * 1979 * NOTE: The bug only happens in GTK 2.6.5 and lower. 1980 */ 1981 return OS.GTK_VERSION < OS.buildVERSION (2, 6, 5) ? 1 : result; 1982 } 1983 1984 override int gtk_motion_notify_event (GtkWidget* widget, GdkEventMotion* event) { 1985 auto window = OS.GDK_EVENT_WINDOW (event); 1986 if (window !is OS.gtk_tree_view_get_bin_window (handle)) return 0; 1987 return super.gtk_motion_notify_event (widget, event); 1988 } 1989 1990 override void gtk_row_activated (GtkTreeView* tree, GtkTreePath* path, GtkTreeViewColumn* column) { 1991 TableItem item = null; 1992 auto indices = OS.gtk_tree_path_get_indices (path); 1993 if (indices !is null) { 1994 item = _getItem (indices [0]); 1995 } 1996 Event event = new Event (); 1997 event.item = item; 1998 postEvent (SWT.DefaultSelection, event); 1999 } 2000 2001 override int gtk_toggled (int renderer, char* pathStr) { 2002 auto path = OS.gtk_tree_path_new_from_string (pathStr); 2003 if (path is null) return 0; 2004 auto indices = OS.gtk_tree_path_get_indices (path); 2005 if (indices !is null) { 2006 TableItem item = _getItem (indices [0]); 2007 item.setChecked (!item.getChecked ()); 2008 Event event = new Event (); 2009 event.detail = SWT.CHECK; 2010 event.item = item; 2011 postEvent (SWT.Selection, event); 2012 } 2013 OS.gtk_tree_path_free (path); 2014 return 0; 2015 } 2016 2017 override void gtk_widget_size_request (GtkWidget* widget, GtkRequisition* requisition) { 2018 /* 2019 * Bug in GTK. For some reason, gtk_widget_size_request() fails 2020 * to include the height of the tree view items when there are 2021 * no columns visible. The fix is to temporarily make one column 2022 * visible. 2023 */ 2024 if (columnCount is 0) { 2025 super.gtk_widget_size_request (widget, requisition); 2026 return; 2027 } 2028 auto columns = OS.gtk_tree_view_get_columns (handle), list = columns; 2029 bool fixVisible = columns !is null; 2030 while (list !is null) { 2031 auto column = OS.g_list_data (list); 2032 if (OS.gtk_tree_view_column_get_visible (column)) { 2033 fixVisible = false; 2034 break; 2035 } 2036 list = OS.g_list_next (list); 2037 } 2038 void* columnHandle; 2039 if (fixVisible) { 2040 columnHandle = OS.g_list_data (columns); 2041 OS.gtk_tree_view_column_set_visible (columnHandle, true); 2042 } 2043 super.gtk_widget_size_request (widget, requisition); 2044 if (fixVisible) { 2045 OS.gtk_tree_view_column_set_visible (columnHandle, false); 2046 } 2047 if (columns !is null) OS.g_list_free (columns); 2048 } 2049 2050 void hideFirstColumn () { 2051 auto firstColumn = OS.gtk_tree_view_get_column (handle, 0); 2052 OS.gtk_tree_view_column_set_visible (firstColumn, false); 2053 } 2054 2055 override void hookEvents () { 2056 super.hookEvents (); 2057 auto selection = OS.gtk_tree_view_get_selection(handle); 2058 OS.g_signal_connect_closure (selection, OS.changed.ptr, display.closures [CHANGED], false); 2059 OS.g_signal_connect_closure (handle, OS.row_activated.ptr, display.closures [ROW_ACTIVATED], false); 2060 if (checkRenderer !is null) { 2061 OS.g_signal_connect_closure (checkRenderer, OS.toggled.ptr, display.closures [TOGGLED], false); 2062 } 2063 } 2064 2065 /** 2066 * Searches the receiver's list starting at the first column 2067 * (index 0) until a column is found that is equal to the 2068 * argument, and returns the index of that column. If no column 2069 * is found, returns -1. 2070 * 2071 * @param column the search column 2072 * @return the index of the column 2073 * 2074 * @exception IllegalArgumentException <ul> 2075 * <li>ERROR_NULL_ARGUMENT - if the column is null</li> 2076 * </ul> 2077 * @exception SWTException <ul> 2078 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2079 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2080 * </ul> 2081 */ 2082 public int indexOf (TableColumn column) { 2083 checkWidget(); 2084 if (column is null) error (SWT.ERROR_NULL_ARGUMENT); 2085 for (int i=0; i<columnCount; i++) { 2086 if (columns [i] is column) return i; 2087 } 2088 return -1; 2089 } 2090 2091 /** 2092 * Searches the receiver's list starting at the first item 2093 * (index 0) until an item is found that is equal to the 2094 * argument, and returns the index of that item. If no item 2095 * is found, returns -1. 2096 * 2097 * @param item the search item 2098 * @return the index of the item 2099 * 2100 * @exception IllegalArgumentException <ul> 2101 * <li>ERROR_NULL_ARGUMENT - if the item is null</li> 2102 * </ul> 2103 * @exception SWTException <ul> 2104 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2105 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2106 * </ul> 2107 */ 2108 public int indexOf (TableItem item) { 2109 checkWidget(); 2110 if (item is null) error (SWT.ERROR_NULL_ARGUMENT); 2111 if (1 <= lastIndexOf && lastIndexOf < itemCount - 1) { 2112 if (items [lastIndexOf] is item) return lastIndexOf; 2113 if (items [lastIndexOf + 1] is item) return ++lastIndexOf; 2114 if (items [lastIndexOf - 1] is item) return --lastIndexOf; 2115 } 2116 if (lastIndexOf < itemCount / 2) { 2117 for (int i=0; i<itemCount; i++) { 2118 if (items [i] is item) return lastIndexOf = i; 2119 } 2120 } else { 2121 for (int i=itemCount - 1; i>=0; --i) { 2122 if (items [i] is item) return lastIndexOf = i; 2123 } 2124 } 2125 return -1; 2126 } 2127 2128 /** 2129 * Returns <code>true</code> if the item is selected, 2130 * and <code>false</code> otherwise. Indices out of 2131 * range are ignored. 2132 * 2133 * @param index the index of the item 2134 * @return the selection state of the item at the index 2135 * 2136 * @exception SWTException <ul> 2137 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2138 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2139 * </ul> 2140 */ 2141 public bool isSelected (int index) { 2142 checkWidget(); 2143 auto selection = OS.gtk_tree_view_get_selection (handle); 2144 char* buffer = toStringz( to!(String)(index)); 2145 auto path = OS.gtk_tree_path_new_from_string (buffer); 2146 bool answer = cast(bool)OS.gtk_tree_selection_path_is_selected (selection, path); 2147 OS.gtk_tree_path_free (path); 2148 return answer; 2149 } 2150 2151 override bool mnemonicHit (wchar key) { 2152 for (int i=0; i<columnCount; i++) { 2153 auto labelHandle = columns [i].labelHandle; 2154 if (labelHandle !is null && mnemonicHit (labelHandle, key)) return true; 2155 } 2156 return false; 2157 } 2158 2159 override bool mnemonicMatch (wchar key) { 2160 for (int i=0; i<columnCount; i++) { 2161 auto labelHandle = columns [i].labelHandle; 2162 if (labelHandle !is null && mnemonicMatch (labelHandle, key)) return true; 2163 } 2164 return false; 2165 } 2166 2167 override GdkDrawable* paintWindow () { 2168 OS.gtk_widget_realize (handle); 2169 return OS.gtk_tree_view_get_bin_window (handle); 2170 } 2171 2172 void recreateRenderers () { 2173 if (checkRenderer !is null) { 2174 display.removeWidget (checkRenderer); 2175 OS.g_object_unref (checkRenderer); 2176 checkRenderer = ownerDraw ? cast(GtkWidget*)OS.g_object_new (display.gtk_cell_renderer_toggle_get_type(), null) : cast(GtkWidget*)OS.gtk_cell_renderer_toggle_new (); 2177 if (checkRenderer is null) error (SWT.ERROR_NO_HANDLES); 2178 OS.g_object_ref (checkRenderer); 2179 display.addWidget (checkRenderer, this); 2180 OS.g_signal_connect_closure (checkRenderer, OS.toggled.ptr, display.closures [TOGGLED], false); 2181 } 2182 if (columnCount is 0) { 2183 createRenderers (OS.gtk_tree_view_get_column (handle, 0), Table.FIRST_COLUMN, true, 0); 2184 } else { 2185 for (int i = 0; i < columnCount; i++) { 2186 TableColumn column = columns [i]; 2187 createRenderers (cast(GtkTreeViewColumn*)column.handle, column.modelIndex, i is 0, column.style); 2188 } 2189 } 2190 } 2191 2192 override void redrawBackgroundImage () { 2193 Control control = findBackgroundControl (); 2194 if (control !is null && control.backgroundImage !is null) { 2195 redrawWidget (0, 0, 0, 0, true, false, false); 2196 } 2197 } 2198 2199 override void register () { 2200 super.register (); 2201 display.addWidget ( cast(GtkWidget*)OS.gtk_tree_view_get_selection (handle), this); 2202 if (checkRenderer !is null) display.addWidget (checkRenderer, this); 2203 } 2204 2205 override void releaseChildren (bool destroy) { 2206 if (items !is null) { 2207 for (int i=0; i<itemCount; i++) { 2208 TableItem item = items [i]; 2209 if (item !is null && !item.isDisposed ()) { 2210 item.release (false); 2211 } 2212 } 2213 items = null; 2214 } 2215 if (columns !is null) { 2216 for (int i=0; i<columnCount; i++) { 2217 TableColumn column = columns [i]; 2218 if (column !is null && !column.isDisposed ()) { 2219 column.release (false); 2220 } 2221 } 2222 columns = null; 2223 } 2224 super.releaseChildren (destroy); 2225 } 2226 2227 override void releaseWidget () { 2228 super.releaseWidget (); 2229 if (modelHandle !is null) OS.g_object_unref (modelHandle); 2230 modelHandle = null; 2231 if (checkRenderer !is null) OS.g_object_unref (checkRenderer); 2232 checkRenderer = null; 2233 if (imageList !is null) imageList.dispose (); 2234 if (headerImageList !is null) headerImageList.dispose (); 2235 imageList = headerImageList = null; 2236 currentItem = null; 2237 } 2238 2239 /** 2240 * Removes the item from the receiver at the given 2241 * zero-relative index. 2242 * 2243 * @param index the index for the item 2244 * 2245 * @exception IllegalArgumentException <ul> 2246 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 2247 * </ul> 2248 * @exception SWTException <ul> 2249 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2250 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2251 * </ul> 2252 */ 2253 public void remove (int index) { 2254 checkWidget(); 2255 if (!(0 <= index && index < itemCount)) error (SWT.ERROR_ITEM_NOT_REMOVED); 2256 GtkTreeIter iter; 2257 TableItem item = items [index]; 2258 bool disposed = false; 2259 if (item !is null) { 2260 disposed = item.isDisposed (); 2261 if (!disposed) { 2262 iter = *cast(GtkTreeIter*)item.handle; 2263 item.release (false); 2264 } 2265 } else { 2266 OS.gtk_tree_model_iter_nth_child (modelHandle, &iter, null, index); 2267 } 2268 if (!disposed) { 2269 auto selection = OS.gtk_tree_view_get_selection (handle); 2270 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2271 OS.gtk_list_store_remove (modelHandle, &iter); 2272 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2273 System.arraycopy (items, index + 1, items, index, --itemCount - index); 2274 items [itemCount] = null; 2275 } 2276 } 2277 2278 /** 2279 * Removes the items from the receiver which are 2280 * between the given zero-relative start and end 2281 * indices (inclusive). 2282 * 2283 * @param start the start of the range 2284 * @param end the end of the range 2285 * 2286 * @exception IllegalArgumentException <ul> 2287 * <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> 2288 * </ul> 2289 * @exception SWTException <ul> 2290 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2291 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2292 * </ul> 2293 */ 2294 public void remove (int start, int end) { 2295 checkWidget(); 2296 if (start > end) return; 2297 if (!(0 <= start && start <= end && end < itemCount)) { 2298 error (SWT.ERROR_INVALID_RANGE); 2299 } 2300 auto selection = OS.gtk_tree_view_get_selection (handle); 2301 GtkTreeIter iter; 2302 OS.gtk_tree_model_iter_nth_child (modelHandle, &iter, null, start); 2303 int index = start; 2304 while (index <= end) { 2305 TableItem item = items [index]; 2306 if (item !is null && !item.isDisposed ()) item.release (false); 2307 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2308 OS.gtk_list_store_remove (modelHandle, &iter); 2309 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2310 index++; 2311 } 2312 System.arraycopy (items, index, items, start, itemCount - index); 2313 for (int i=itemCount-(index-start); i<itemCount; i++) items [i] = null; 2314 itemCount = itemCount - (index - start); 2315 } 2316 2317 /** 2318 * Removes the items from the receiver's list at the given 2319 * zero-relative indices. 2320 * 2321 * @param indices the array of indices of the items 2322 * 2323 * @exception IllegalArgumentException <ul> 2324 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 2325 * </ul> 2326 * @exception SWTException <ul> 2327 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2328 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2329 * </ul> 2330 */ 2331 public void remove (int [] indices) { 2332 checkWidget(); 2333 // SWT extension: allow null for zero length string 2334 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 2335 if (indices.length is 0) return; 2336 int [] newIndices = new int [indices.length]; 2337 System.arraycopy (indices, 0, newIndices, 0, indices.length); 2338 sort (newIndices); 2339 int start = newIndices [newIndices.length - 1], end = newIndices [0]; 2340 if (!(0 <= start && start <= end && end < itemCount)) { 2341 error (SWT.ERROR_INVALID_RANGE); 2342 } 2343 auto selection = OS.gtk_tree_view_get_selection (handle); 2344 int last = -1; 2345 GtkTreeIter iter; 2346 for (int i=0; i<newIndices.length; i++) { 2347 int index = newIndices [i]; 2348 if (index !is last) { 2349 TableItem item = items [index]; 2350 bool disposed = false; 2351 if (item !is null) { 2352 disposed = item.isDisposed (); 2353 if (!disposed) { 2354 iter = *cast(GtkTreeIter*) item.handle; 2355 item.release (false); 2356 } 2357 } else { 2358 OS.gtk_tree_model_iter_nth_child (modelHandle, &iter, null, index); 2359 } 2360 if (!disposed) { 2361 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2362 OS.gtk_list_store_remove (modelHandle, &iter); 2363 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2364 System.arraycopy (items, index + 1, items, index, --itemCount - index); 2365 items [itemCount] = null; 2366 } 2367 last = index; 2368 } 2369 } 2370 } 2371 2372 /** 2373 * Removes all of the items from the receiver. 2374 * 2375 * @exception SWTException <ul> 2376 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2377 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2378 * </ul> 2379 */ 2380 public void removeAll () { 2381 checkWidget(); 2382 int index = itemCount - 1; 2383 while (index >= 0) { 2384 TableItem item = items [index]; 2385 if (item !is null && !item.isDisposed ()) item.release (false); 2386 --index; 2387 } 2388 items = new TableItem [4]; 2389 itemCount = 0; 2390 auto selection = OS.gtk_tree_view_get_selection (handle); 2391 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2392 OS.gtk_list_store_clear (modelHandle); 2393 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2394 2395 resetCustomDraw (); 2396 /* Disable searching when using VIRTUAL */ 2397 if ((style & SWT.VIRTUAL) !is 0) { 2398 /* 2399 * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE) 2400 * would prevent the user from being able to type in text to search the tree. 2401 * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive 2402 * search. This meant that even if FALSE was passed to enable_search, the user 2403 * can still bring up the search pop up using the keybinding. GTK also introduced 2404 * the notion of passing a -1 to gtk_set_search_column to disable searching 2405 * (including the search key binding). The fix is to use the right calls 2406 * for the right version. 2407 */ 2408 if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)){ 2409 OS.gtk_tree_view_set_search_column (handle, -1); 2410 } else { 2411 OS.gtk_tree_view_set_enable_search (handle, false); 2412 } 2413 } else { 2414 /* Set the search column whenever the model changes */ 2415 int firstColumn = columnCount is 0 ? FIRST_COLUMN : columns [0].modelIndex; 2416 OS.gtk_tree_view_set_search_column (handle, firstColumn + CELL_TEXT); 2417 } 2418 } 2419 2420 /** 2421 * Removes the listener from the collection of listeners who will 2422 * be notified when the user changes the receiver's selection. 2423 * 2424 * @param listener the listener which should no longer be notified 2425 * 2426 * @exception IllegalArgumentException <ul> 2427 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 2428 * </ul> 2429 * @exception SWTException <ul> 2430 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2431 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2432 * </ul> 2433 * 2434 * @see SelectionListener 2435 * @see #addSelectionListener(SelectionListener) 2436 */ 2437 public void removeSelectionListener(SelectionListener listener) { 2438 checkWidget(); 2439 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 2440 if (eventTable is null) return; 2441 eventTable.unhook (SWT.Selection, listener); 2442 eventTable.unhook (SWT.DefaultSelection,listener); 2443 } 2444 2445 override void rendererGetSizeProc ( 2446 GtkCellRenderer *cell, 2447 GtkWidget *widget, 2448 GdkRectangle *cell_area, 2449 int *x_offset, 2450 int *y_offset, 2451 int *width, 2452 int *height) 2453 { 2454 auto g_class = OS.g_type_class_peek_parent (OS.G_OBJECT_GET_CLASS (cell)); 2455 GtkCellRendererClass* klass = cast(GtkCellRendererClass*)g_class; 2456 klass.get_size( cell, handle, cell_area, x_offset, y_offset, width, height); 2457 if (!ignoreSize && OS.GTK_IS_CELL_RENDERER_TEXT (cell)) { 2458 auto iter = OS.g_object_get_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX2); 2459 TableItem item = null; 2460 if (iter !is null) { 2461 auto path = OS.gtk_tree_model_get_path (modelHandle, iter); 2462 auto buffer = OS.gtk_tree_path_get_indices (path); 2463 int index = buffer [0]; 2464 item = _getItem (index); 2465 OS.gtk_tree_path_free (path); 2466 } 2467 if (item !is null) { 2468 int columnIndex = 0; 2469 if (columnCount > 0) { 2470 auto columnHandle = OS.g_object_get_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX1); 2471 for (int i = 0; i < columnCount; i++) { 2472 if (columns [i].handle is columnHandle) { 2473 columnIndex = i; 2474 break; 2475 } 2476 } 2477 } 2478 if (hooks (SWT.MeasureItem)) { 2479 int contentWidth, contentHeight; 2480 if (width !is null) contentWidth = *width; 2481 if (height !is null) contentHeight = *height; 2482 Image image = item.getImage (columnIndex); 2483 int imageWidth = 0; 2484 if (image !is null) { 2485 Rectangle bounds = image.getBounds (); 2486 imageWidth = bounds.width; 2487 } 2488 contentWidth += imageWidth; 2489 GC gc = new GC (this); 2490 gc.setFont (item.getFont (columnIndex)); 2491 Event event = new Event (); 2492 event.item = item; 2493 event.index = columnIndex; 2494 event.gc = gc; 2495 event.width = contentWidth; 2496 event.height = contentHeight; 2497 sendEvent (SWT.MeasureItem, event); 2498 gc.dispose (); 2499 contentWidth = event.width - imageWidth; 2500 if (contentHeight < event.height) contentHeight = event.height; 2501 if (width !is null) *width = contentWidth; 2502 if (height !is null) *height = contentHeight; 2503 } 2504 } 2505 } 2506 } 2507 2508 override void rendererRenderProc ( 2509 GtkCellRenderer * cell, 2510 GdkDrawable * window, 2511 GtkWidget * widget, 2512 GdkRectangle *background_area, 2513 GdkRectangle *cell_area, 2514 GdkRectangle *expose_area, 2515 int flags) 2516 { 2517 TableItem item = null; 2518 auto iter = OS.g_object_get_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX2); 2519 if (iter !is null) { 2520 auto path = OS.gtk_tree_model_get_path (modelHandle, iter); 2521 auto buffer = OS.gtk_tree_path_get_indices (path); 2522 int index = buffer [0]; 2523 item = _getItem (index); 2524 OS.gtk_tree_path_free (path); 2525 } 2526 auto columnHandle = OS.g_object_get_qdata (cast(GObject*)cell, Display.SWT_OBJECT_INDEX1); 2527 int columnIndex = 0; 2528 if (columnCount > 0) { 2529 for (int i = 0; i < columnCount; i++) { 2530 if (columns [i].handle is columnHandle) { 2531 columnIndex = i; 2532 break; 2533 } 2534 } 2535 } 2536 if (item !is null) { 2537 if (OS.GTK_IS_CELL_RENDERER_TOGGLE (cell) || (OS.GTK_IS_CELL_RENDERER_PIXBUF (cell) && (columnIndex !is 0 || (style & SWT.CHECK) is 0))) { 2538 drawFlags = flags; 2539 drawState = SWT.FOREGROUND; 2540 void* ptr; 2541 OS.gtk_tree_model_get1 (modelHandle, item.handle, Table.BACKGROUND_COLUMN, &ptr); 2542 if (ptr is null) { 2543 int modelIndex = columnCount is 0 ? Table.FIRST_COLUMN : columns [columnIndex].modelIndex; 2544 OS.gtk_tree_model_get1 (modelHandle, item.handle, modelIndex + Table.CELL_BACKGROUND, &ptr); 2545 } 2546 if (ptr !is null) drawState |= SWT.BACKGROUND; 2547 if ((flags & OS.GTK_CELL_RENDERER_SELECTED) !is 0) drawState |= SWT.SELECTED; 2548 if ((flags & OS.GTK_CELL_RENDERER_FOCUSED) !is 0) drawState |= SWT.FOCUSED; 2549 2550 GdkRectangle rect; 2551 auto path = OS.gtk_tree_model_get_path (modelHandle, iter); 2552 OS.gtk_tree_view_get_background_area (handle, path, columnHandle, &rect); 2553 OS.gtk_tree_path_free (path); 2554 2555 if ((drawState & SWT.SELECTED) is 0) { 2556 Control control = findBackgroundControl (); 2557 if (control !is null && control.backgroundImage !is null) { 2558 OS.gdk_window_clear_area (window, rect.x, rect.y, rect.width, rect.height); 2559 } 2560 } 2561 2562 if (hooks (SWT.EraseItem)) { 2563 bool wasSelected = false; 2564 if ((drawState & SWT.SELECTED) !is 0) { 2565 wasSelected = true; 2566 OS.gdk_window_clear_area (window, rect.x, rect.y, rect.width, rect.height); 2567 } 2568 GC gc = new GC (this); 2569 if ((drawState & SWT.SELECTED) !is 0) { 2570 gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION)); 2571 gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT)); 2572 } else { 2573 gc.setBackground (item.getBackground (columnIndex)); 2574 gc.setForeground (item.getForeground (columnIndex)); 2575 } 2576 gc.setFont (item.getFont (columnIndex)); 2577 if ((style & SWT.MIRRORED) !is 0) rect.x = getClientWidth () - rect.width - rect.x; 2578 gc.setClipping (rect.x, rect.y, rect.width, rect.height); 2579 Event event = new Event (); 2580 event.item = item; 2581 event.index = columnIndex; 2582 event.gc = gc; 2583 event.x = rect.x; 2584 event.y = rect.y; 2585 event.width = rect.width; 2586 event.height = rect.height; 2587 event.detail = drawState; 2588 sendEvent (SWT.EraseItem, event); 2589 drawForeground = null; 2590 drawState = event.doit ? event.detail : 0; 2591 drawFlags &= ~(OS.GTK_CELL_RENDERER_FOCUSED | OS.GTK_CELL_RENDERER_SELECTED); 2592 if ((drawState & SWT.SELECTED) !is 0) drawFlags |= OS.GTK_CELL_RENDERER_SELECTED; 2593 if ((drawState & SWT.FOCUSED) !is 0) drawFlags |= OS.GTK_CELL_RENDERER_FOCUSED; 2594 if ((drawState & SWT.SELECTED) !is 0) { 2595 auto style = OS.gtk_widget_get_style (widget); 2596 //TODO - parity and sorted 2597 OS.gtk_paint_flat_box (style, window, OS.GTK_STATE_SELECTED, OS.GTK_SHADOW_NONE, &rect, widget, "cell_odd".ptr, rect.x, rect.y, rect.width, rect.height); 2598 } else { 2599 if (wasSelected) drawForeground = gc.getForeground ().handle; 2600 } 2601 gc.dispose(); 2602 } 2603 } 2604 } 2605 int result = 0; 2606 if ((drawState & SWT.BACKGROUND) !is 0 && (drawState & SWT.SELECTED) is 0) { 2607 GC gc = new GC (this); 2608 gc.setBackground (item.getBackground (columnIndex)); 2609 gc.fillRectangle (background_area.x, background_area.y, background_area.width, background_area.height); 2610 gc.dispose (); 2611 } 2612 if ((drawState & SWT.FOREGROUND) !is 0 || OS.GTK_IS_CELL_RENDERER_TOGGLE (cell)) { 2613 auto g_class = OS.g_type_class_peek_parent (OS.G_OBJECT_GET_CLASS (cell)); 2614 GtkCellRendererClass* klass = cast(GtkCellRendererClass*)g_class; 2615 if (drawForeground !is null && OS.GTK_IS_CELL_RENDERER_TEXT (cell)) { 2616 OS.g_object_set1 (cell, OS.foreground_gdk.ptr, cast(int)drawForeground); 2617 } 2618 klass.render( cell, window, widget, background_area, cell_area, expose_area, drawFlags); 2619 } 2620 if (item !is null) { 2621 if (OS.GTK_IS_CELL_RENDERER_TEXT (cell)) { 2622 if (hooks (SWT.PaintItem)) { 2623 GdkRectangle rect; 2624 auto path = OS.gtk_tree_model_get_path (modelHandle, iter); 2625 OS.gtk_tree_view_get_background_area (handle, path, columnHandle, &rect); 2626 OS.gtk_tree_path_free (path); 2627 ignoreSize = true; 2628 int contentX, contentWidth; 2629 OS.gtk_cell_renderer_get_size (cell, handle, null, null, null, &contentWidth, null); 2630 OS.gtk_tree_view_column_cell_get_position (columnHandle, cell, &contentX, null); 2631 ignoreSize = false; 2632 Image image = item.getImage (columnIndex); 2633 int imageWidth = 0; 2634 if (image !is null) { 2635 Rectangle bounds = image.getBounds (); 2636 imageWidth = bounds.width; 2637 } 2638 contentX -= imageWidth; 2639 contentWidth += imageWidth; 2640 GC gc = new GC (this); 2641 if ((drawState & SWT.SELECTED) !is 0) { 2642 gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION)); 2643 gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT)); 2644 } else { 2645 gc.setBackground (item.getBackground (columnIndex)); 2646 Color foreground = drawForeground !is null ? Color.gtk_new (display, drawForeground) : item.getForeground (columnIndex); 2647 gc.setForeground (foreground); 2648 } 2649 gc.setFont (item.getFont (columnIndex)); 2650 if ((style & SWT.MIRRORED) !is 0) rect.x = getClientWidth () - rect.width - rect.x; 2651 gc.setClipping (rect.x, rect.y, rect.width, rect.height); 2652 Event event = new Event (); 2653 event.item = item; 2654 event.index = columnIndex; 2655 event.gc = gc; 2656 event.x = rect.x + contentX; 2657 event.y = rect.y; 2658 event.width = contentWidth; 2659 event.height = rect.height; 2660 event.detail = drawState; 2661 sendEvent (SWT.PaintItem, event); 2662 gc.dispose(); 2663 } 2664 } 2665 } 2666 } 2667 2668 void resetCustomDraw () { 2669 if ((style & SWT.VIRTUAL) !is 0 || ownerDraw) return; 2670 int end = Math.max (1, columnCount); 2671 for (int i=0; i<end; i++) { 2672 bool customDraw = columnCount !is 0 ? columns [i].customDraw : firstCustomDraw; 2673 if (customDraw) { 2674 auto column = OS.gtk_tree_view_get_column (handle, i); 2675 auto textRenderer = getTextRenderer (column); 2676 display.doCellDataProc( null, cast(GtkTreeViewColumn*)column, cast(GtkCellRenderer*)textRenderer ); 2677 if (columnCount !is 0) columns [i].customDraw = false; 2678 } 2679 } 2680 firstCustomDraw = false; 2681 } 2682 2683 /** 2684 * Selects the item at the given zero-relative index in the receiver. 2685 * If the item at the index was already selected, it remains 2686 * selected. Indices that are out of range are ignored. 2687 * 2688 * @param index the index of the item to select 2689 * 2690 * @exception SWTException <ul> 2691 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2692 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2693 * </ul> 2694 */ 2695 public void select (int index) { 2696 checkWidget(); 2697 if (!(0 <= index && index < itemCount)) return; 2698 bool fixColumn = showFirstColumn (); 2699 auto selection = OS.gtk_tree_view_get_selection (handle); 2700 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2701 TableItem item = _getItem (index); 2702 OS.gtk_tree_selection_select_iter (selection, item.handle); 2703 if ((style & SWT.SINGLE) !is 0) { 2704 auto path = OS.gtk_tree_model_get_path (modelHandle, item.handle); 2705 OS.gtk_tree_view_set_cursor (handle, path, null, false); 2706 OS.gtk_tree_path_free (path); 2707 } 2708 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2709 if (fixColumn) hideFirstColumn (); 2710 } 2711 2712 /** 2713 * Selects the items in the range specified by the given zero-relative 2714 * indices in the receiver. The range of indices is inclusive. 2715 * The current selection is not cleared before the new items are selected. 2716 * <p> 2717 * If an item in the given range is not selected, it is selected. 2718 * If an item in the given range was already selected, it remains selected. 2719 * Indices that are out of range are ignored and no items will be selected 2720 * if start is greater than end. 2721 * If the receiver is single-select and there is more than one item in the 2722 * given range, then all indices are ignored. 2723 * </p> 2724 * 2725 * @param start the start of the range 2726 * @param end the end of the range 2727 * 2728 * @exception SWTException <ul> 2729 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2730 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2731 * </ul> 2732 * 2733 * @see Table#setSelection(int,int) 2734 */ 2735 public void select (int start, int end) { 2736 checkWidget (); 2737 if (end < 0 || start > end || ((style & SWT.SINGLE) !is 0 && start !is end)) return; 2738 if (itemCount is 0 || start >= itemCount) return; 2739 start = Math.max (0, start); 2740 end = Math.min (end, itemCount - 1); 2741 bool fixColumn = showFirstColumn (); 2742 auto selection = OS.gtk_tree_view_get_selection (handle); 2743 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2744 for (int index=start; index<=end; index++) { 2745 TableItem item = _getItem (index); 2746 OS.gtk_tree_selection_select_iter (selection, item.handle); 2747 if ((style & SWT.SINGLE) !is 0) { 2748 auto path = OS.gtk_tree_model_get_path (modelHandle, item.handle); 2749 OS.gtk_tree_view_set_cursor (handle, path, null, false); 2750 OS.gtk_tree_path_free (path); 2751 } 2752 } 2753 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2754 if (fixColumn) hideFirstColumn (); 2755 } 2756 2757 /** 2758 * Selects the items at the given zero-relative indices in the receiver. 2759 * The current selection is not cleared before the new items are selected. 2760 * <p> 2761 * If the item at a given index is not selected, it is selected. 2762 * If the item at a given index was already selected, it remains selected. 2763 * Indices that are out of range and duplicate indices are ignored. 2764 * If the receiver is single-select and multiple indices are specified, 2765 * then all indices are ignored. 2766 * </p> 2767 * 2768 * @param indices the array of indices for the items to select 2769 * 2770 * @exception SWTException <ul> 2771 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2772 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2773 * </ul> 2774 * 2775 * @see Table#setSelection(int[]) 2776 */ 2777 public void select (int [] indices) { 2778 checkWidget (); 2779 // SWT extension: allow null for zero length string 2780 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 2781 ptrdiff_t length = indices.length; 2782 if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return; 2783 bool fixColumn = showFirstColumn (); 2784 auto selection = OS.gtk_tree_view_get_selection (handle); 2785 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2786 for (int i=0; i<length; i++) { 2787 int index = indices [i]; 2788 if (!(0 <= index && index < itemCount)) continue; 2789 TableItem item = _getItem (index); 2790 OS.gtk_tree_selection_select_iter (selection, item.handle); 2791 if ((style & SWT.SINGLE) !is 0) { 2792 auto path = OS.gtk_tree_model_get_path (modelHandle, item.handle); 2793 OS.gtk_tree_view_set_cursor (handle, path, null, false); 2794 OS.gtk_tree_path_free (path); 2795 } 2796 } 2797 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2798 if (fixColumn) hideFirstColumn (); 2799 } 2800 2801 /** 2802 * Selects all of the items in the receiver. 2803 * <p> 2804 * If the receiver is single-select, do nothing. 2805 * </p> 2806 * 2807 * @exception SWTException <ul> 2808 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2809 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2810 * </ul> 2811 */ 2812 public void selectAll () { 2813 checkWidget(); 2814 if ((style & SWT.SINGLE) !is 0) return; 2815 bool fixColumn = showFirstColumn (); 2816 auto selection = OS.gtk_tree_view_get_selection (handle); 2817 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2818 OS.gtk_tree_selection_select_all (selection); 2819 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2820 if (fixColumn) hideFirstColumn (); 2821 } 2822 2823 void selectFocusIndex (int index) { 2824 /* 2825 * Note that this method both selects and sets the focus to the 2826 * specified index, so any previous selection in the list will be lost. 2827 * gtk does not provide a way to just set focus to a specified list item. 2828 */ 2829 if (!(0 <= index && index < itemCount)) return; 2830 TableItem item = _getItem (index); 2831 auto path = OS.gtk_tree_model_get_path (modelHandle, item.handle); 2832 auto selection = OS.gtk_tree_view_get_selection (handle); 2833 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2834 OS.gtk_tree_view_set_cursor (handle, path, null, false); 2835 /* 2836 * Bug in GTK. For some reason, when an event loop is run from 2837 * within a key pressed handler and a dialog is displayed that 2838 * contains a GtkTreeView, gtk_tree_view_set_cursor() does 2839 * not set the cursor or select the item. The fix is to select the 2840 * item with gtk_tree_selection_select_iter() as well. 2841 * 2842 * NOTE: This happens in GTK 2.2.1 and is fixed in GTK 2.2.4. 2843 */ 2844 OS.gtk_tree_selection_select_iter (selection, item.handle); 2845 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 2846 OS.gtk_tree_path_free (path); 2847 } 2848 2849 override void setBackgroundColor (GdkColor* color) { 2850 super.setBackgroundColor (color); 2851 OS.gtk_widget_modify_base (handle, 0, color); 2852 } 2853 2854 override void setBackgroundPixmap (GdkPixmap* pixmap) { 2855 super.setBackgroundPixmap (pixmap); 2856 auto window = paintWindow (); 2857 if (window !is null) OS.gdk_window_set_back_pixmap (window, null, true); 2858 } 2859 2860 override int setBounds (int x, int y, int width, int height, bool move, bool resize) { 2861 int result = super.setBounds (x, y, width, height, move, resize); 2862 /* 2863 * Bug on GTK. The tree view sometimes does not get a paint 2864 * event or resizes to a one pixel square when resized in a new 2865 * shell that is not visible after any event loop has been run. The 2866 * problem is intermittent. It doesn't seem to happen the first time 2867 * a new shell is created. The fix is to ensure the tree view is realized 2868 * after it has been resized. 2869 */ 2870 OS.gtk_widget_realize (handle); 2871 /* 2872 * Bug in GTK. An empty GtkTreeView fails to repaint the focus rectangle 2873 * correctly when resized on versions before 2.6.0. The fix is to force 2874 * the widget to redraw. 2875 */ 2876 if (OS.GTK_VERSION < OS.buildVERSION (2, 6, 0) && itemCount is 0) redraw (false); 2877 return result; 2878 } 2879 2880 /** 2881 * Sets the order that the items in the receiver should 2882 * be displayed in to the given argument which is described 2883 * in terms of the zero-relative ordering of when the items 2884 * were added. 2885 * 2886 * @param order the new order to display the items 2887 * 2888 * @exception SWTException <ul> 2889 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2890 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2891 * </ul> 2892 * @exception IllegalArgumentException <ul> 2893 * <li>ERROR_INVALID_ARGUMENT - if the item order is not the same length as the number of items</li> 2894 * </ul> 2895 * 2896 * @see Table#getColumnOrder() 2897 * @see TableColumn#getMoveable() 2898 * @see TableColumn#setMoveable(bool) 2899 * @see SWT#Move 2900 * 2901 * @since 3.1 2902 */ 2903 public void setColumnOrder (int [] order) { 2904 checkWidget (); 2905 // SWT extension: allow null for zero length string 2906 //if (order is null) error (SWT.ERROR_NULL_ARGUMENT); 2907 if (columnCount is 0) { 2908 if (order.length > 0) error (SWT.ERROR_INVALID_ARGUMENT); 2909 return; 2910 } 2911 if (order.length !is columnCount) error (SWT.ERROR_INVALID_ARGUMENT); 2912 bool [] seen = new bool [columnCount]; 2913 for (int i = 0; i<order.length; i++) { 2914 int index = order [i]; 2915 if (index < 0 || index >= columnCount) error (SWT.ERROR_INVALID_RANGE); 2916 if (seen [index]) error (SWT.ERROR_INVALID_ARGUMENT); 2917 seen [index] = true; 2918 } 2919 for (int i=0; i<order.length; i++) { 2920 auto column = columns [order [i]].handle; 2921 auto baseColumn = i is 0 ? null : columns [order [i-1]].handle; 2922 OS.gtk_tree_view_move_column_after (handle, column, baseColumn); 2923 } 2924 } 2925 2926 override void setFontDescription (PangoFontDescription* font) { 2927 super.setFontDescription (font); 2928 TableColumn[] columns = getColumns (); 2929 for (int i = 0; i < columns.length; i++) { 2930 if (columns[i] !is null) { 2931 columns[i].setFontDescription (font); 2932 } 2933 } 2934 } 2935 2936 /** 2937 * Marks the receiver's header as visible if the argument is <code>true</code>, 2938 * and marks it invisible otherwise. 2939 * <p> 2940 * If one of the receiver's ancestors is not visible or some 2941 * other condition makes the receiver not visible, marking 2942 * it visible may not actually cause it to be displayed. 2943 * </p> 2944 * 2945 * @param show the new visibility state 2946 * 2947 * @exception SWTException <ul> 2948 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2949 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2950 * </ul> 2951 */ 2952 public void setHeaderVisible (bool show) { 2953 checkWidget (); 2954 OS.gtk_tree_view_set_headers_visible (handle, show); 2955 } 2956 2957 /** 2958 * Sets the number of items contained in the receiver. 2959 * 2960 * @param count the number of items 2961 * 2962 * @exception SWTException <ul> 2963 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 2964 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 2965 * </ul> 2966 * 2967 * @since 3.0 2968 */ 2969 public void setItemCount (int count) { 2970 checkWidget (); 2971 count = Math.max (0, count); 2972 if (count is itemCount) return; 2973 bool isVirtual = (style & SWT.VIRTUAL) !is 0; 2974 if (!isVirtual) setRedraw (false); 2975 remove (count, itemCount - 1); 2976 int length = Math.max (4, (count + 3) / 4 * 4); 2977 TableItem [] newItems = new TableItem [length]; 2978 System.arraycopy (items, 0, newItems, 0, itemCount); 2979 items = newItems; 2980 if (isVirtual) { 2981 GtkTreeIter iter; 2982 for (int i=itemCount; i<count; i++) { 2983 OS.gtk_list_store_append (modelHandle, &iter); 2984 } 2985 itemCount = count; 2986 } else { 2987 for (int i=itemCount; i<count; i++) { 2988 new TableItem (this, SWT.NONE, i, true); 2989 } 2990 } 2991 if (!isVirtual) setRedraw (true); 2992 } 2993 2994 /** 2995 * Marks the receiver's lines as visible if the argument is <code>true</code>, 2996 * and marks it invisible otherwise. 2997 * <p> 2998 * If one of the receiver's ancestors is not visible or some 2999 * other condition makes the receiver not visible, marking 3000 * it visible may not actually cause it to be displayed. 3001 * </p> 3002 * 3003 * @param show the new visibility state 3004 * 3005 * @exception SWTException <ul> 3006 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3007 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3008 * </ul> 3009 */ 3010 public void setLinesVisible (bool show) { 3011 checkWidget(); 3012 OS.gtk_tree_view_set_rules_hint (handle, show); 3013 } 3014 3015 override void setParentBackground () { 3016 super.setParentBackground (); 3017 auto window = paintWindow (); 3018 if (window !is null) OS.gdk_window_set_back_pixmap (window, null, true); 3019 } 3020 3021 override void setParentWindow (GtkWidget* widget) { 3022 auto window = eventWindow (); 3023 OS.gtk_widget_set_parent_window (widget, window); 3024 } 3025 3026 override public void setRedraw (bool redraw) { 3027 checkWidget(); 3028 super.setRedraw (redraw); 3029 if (redraw && drawCount is 0) { 3030 /* Resize the item array to match the item count */ 3031 if (items.length > 4 && items.length - itemCount > 3) { 3032 int length = Math.max (4, (itemCount + 3) / 4 * 4); 3033 TableItem [] newItems = new TableItem [length]; 3034 System.arraycopy (items, 0, newItems, 0, itemCount); 3035 items = newItems; 3036 } 3037 } 3038 } 3039 3040 void setScrollWidth (GtkTreeViewColumn* column, TableItem item) { 3041 if (columnCount !is 0 || currentItem is item) return; 3042 /* 3043 * Use GTK_TREE_VIEW_COLUMN_GROW_ONLY on GTK versions < 2.3.2 3044 * because fixed_height_mode is not supported. 3045 */ 3046 if (((style & SWT.VIRTUAL) !is 0) && OS.GTK_VERSION < OS.buildVERSION (2, 3, 2)) return; 3047 int width = OS.gtk_tree_view_column_get_fixed_width (column); 3048 int itemWidth = calculateWidth (column, cast(GtkTreeIter*)item.handle); 3049 if (width < itemWidth) { 3050 OS.gtk_tree_view_column_set_fixed_width (column, itemWidth); 3051 } 3052 } 3053 3054 /** 3055 * Sets the column used by the sort indicator for the receiver. A null 3056 * value will clear the sort indicator. The current sort column is cleared 3057 * before the new column is set. 3058 * 3059 * @param column the column used by the sort indicator or <code>null</code> 3060 * 3061 * @exception IllegalArgumentException <ul> 3062 * <li>ERROR_INVALID_ARGUMENT - if the column is disposed</li> 3063 * </ul> 3064 * @exception SWTException <ul> 3065 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3066 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3067 * </ul> 3068 * 3069 * @since 3.2 3070 */ 3071 public void setSortColumn (TableColumn column) { 3072 checkWidget (); 3073 if (column !is null && column.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); 3074 if (sortColumn !is null && !sortColumn.isDisposed()) { 3075 OS.gtk_tree_view_column_set_sort_indicator (sortColumn.handle, false); 3076 } 3077 sortColumn = column; 3078 if (sortColumn !is null && sortDirection !is SWT.NONE) { 3079 OS.gtk_tree_view_column_set_sort_indicator (sortColumn.handle, true); 3080 OS.gtk_tree_view_column_set_sort_order (sortColumn.handle, sortDirection is SWT.DOWN ? 0 : 1); 3081 } 3082 } 3083 3084 /** 3085 * Sets the direction of the sort indicator for the receiver. The value 3086 * can be one of <code>UP</code>, <code>DOWN</code> or <code>NONE</code>. 3087 * 3088 * @param direction the direction of the sort indicator 3089 * 3090 * @exception SWTException <ul> 3091 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3092 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3093 * </ul> 3094 * 3095 * @since 3.2 3096 */ 3097 public void setSortDirection (int direction) { 3098 checkWidget (); 3099 if (direction !is SWT.UP && direction !is SWT.DOWN && direction !is SWT.NONE) return; 3100 sortDirection = direction; 3101 if (sortColumn is null || sortColumn.isDisposed ()) return; 3102 if (sortDirection is SWT.NONE) { 3103 OS.gtk_tree_view_column_set_sort_indicator (sortColumn.handle, false); 3104 } else { 3105 OS.gtk_tree_view_column_set_sort_indicator (sortColumn.handle, true); 3106 OS.gtk_tree_view_column_set_sort_order (sortColumn.handle, sortDirection is SWT.DOWN ? 0 : 1); 3107 } 3108 } 3109 3110 /** 3111 * Selects the item at the given zero-relative index in the receiver. 3112 * The current selection is first cleared, then the new item is selected. 3113 * 3114 * @param index the index of the item to select 3115 * 3116 * @exception SWTException <ul> 3117 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3118 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3119 * </ul> 3120 * 3121 * @see Table#deselectAll() 3122 * @see Table#selectcast(int) 3123 */ 3124 public void setSelection (int index) { 3125 checkWidget (); 3126 bool fixColumn = showFirstColumn (); 3127 deselectAll (); 3128 selectFocusIndex (index); 3129 showSelection (); 3130 if (fixColumn) hideFirstColumn (); 3131 } 3132 3133 /** 3134 * Selects the items in the range specified by the given zero-relative 3135 * indices in the receiver. The range of indices is inclusive. 3136 * The current selection is cleared before the new items are selected. 3137 * <p> 3138 * Indices that are out of range are ignored and no items will be selected 3139 * if start is greater than end. 3140 * If the receiver is single-select and there is more than one item in the 3141 * given range, then all indices are ignored. 3142 * </p> 3143 * 3144 * @param start the start index of the items to select 3145 * @param end the end index of the items to select 3146 * 3147 * @exception SWTException <ul> 3148 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3149 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3150 * </ul> 3151 * 3152 * @see Table#deselectAll() 3153 * @see Table#select(int,int) 3154 */ 3155 public void setSelection (int start, int end) { 3156 checkWidget (); 3157 deselectAll(); 3158 if (end < 0 || start > end || ((style & SWT.SINGLE) !is 0 && start !is end)) return; 3159 if (itemCount is 0 || start >= itemCount) return; 3160 bool fixColumn = showFirstColumn (); 3161 start = Math.max (0, start); 3162 end = Math.min (end, itemCount - 1); 3163 selectFocusIndex (start); 3164 if ((style & SWT.MULTI) !is 0) { 3165 select (start, end); 3166 } 3167 showSelection (); 3168 if (fixColumn) hideFirstColumn (); 3169 } 3170 3171 /** 3172 * Selects the items at the given zero-relative indices in the receiver. 3173 * The current selection is cleared before the new items are selected. 3174 * <p> 3175 * Indices that are out of range and duplicate indices are ignored. 3176 * If the receiver is single-select and multiple indices are specified, 3177 * then all indices are ignored. 3178 * </p> 3179 * 3180 * @param indices the indices of the items to select 3181 * 3182 * @exception SWTException <ul> 3183 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3184 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3185 * </ul> 3186 * 3187 * @see Table#deselectAll() 3188 * @see Table#select(int[]) 3189 */ 3190 public void setSelection (int [] indices) { 3191 checkWidget (); 3192 // SWT extension: allow null for zero length string 3193 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 3194 deselectAll (); 3195 ptrdiff_t length = indices.length; 3196 if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return; 3197 bool fixColumn = showFirstColumn (); 3198 selectFocusIndex (indices [0]); 3199 if ((style & SWT.MULTI) !is 0) { 3200 select (indices); 3201 } 3202 showSelection (); 3203 if (fixColumn) hideFirstColumn (); 3204 } 3205 3206 /** 3207 * Sets the receiver's selection to the given item. 3208 * The current selection is cleared before the new item is selected. 3209 * <p> 3210 * If the item is not in the receiver, then it is ignored. 3211 * </p> 3212 * 3213 * @param item the item to select 3214 * 3215 * @exception IllegalArgumentException <ul> 3216 * <li>ERROR_NULL_ARGUMENT - if the item is null</li> 3217 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> 3218 * </ul> 3219 * @exception SWTException <ul> 3220 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3221 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3222 * </ul> 3223 * 3224 * @since 3.2 3225 */ 3226 public void setSelection (TableItem item) { 3227 if (item is null) error (SWT.ERROR_NULL_ARGUMENT); 3228 setSelection ( [item] ); 3229 } 3230 3231 3232 /** 3233 * Sets the receiver's selection to be the given array of items. 3234 * The current selection is cleared before the new items are selected. 3235 * <p> 3236 * Items that are not in the receiver are ignored. 3237 * If the receiver is single-select and multiple items are specified, 3238 * then all items are ignored. 3239 * </p> 3240 * 3241 * @param items the array of items 3242 * 3243 * @exception IllegalArgumentException <ul> 3244 * <li>ERROR_INVALID_ARGUMENT - if one of the items has been disposed</li> 3245 * </ul> 3246 * @exception SWTException <ul> 3247 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3248 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3249 * </ul> 3250 * 3251 * @see Table#deselectAll() 3252 * @see Table#select(int[]) 3253 * @see Table#setSelection(int[]) 3254 */ 3255 public void setSelection (TableItem [] items) { 3256 checkWidget (); 3257 // SWT extension: allow null for zero length string 3258 //if (items is null) error (SWT.ERROR_NULL_ARGUMENT); 3259 bool fixColumn = showFirstColumn (); 3260 deselectAll (); 3261 ptrdiff_t length = items.length; 3262 if (!(length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1))) { 3263 bool first = true; 3264 for (int i = 0; i < length; i++) { 3265 int index = indexOf (items [i]); 3266 if (index !is -1) { 3267 if (first) { 3268 first = false; 3269 selectFocusIndex (index); 3270 } else { 3271 select (index); 3272 } 3273 } 3274 } 3275 showSelection (); 3276 } 3277 if (fixColumn) hideFirstColumn (); 3278 } 3279 3280 /** 3281 * Sets the zero-relative index of the item which is currently 3282 * at the top of the receiver. This index can change when items 3283 * are scrolled or new items are added and removed. 3284 * 3285 * @param index the index of the top item 3286 * 3287 * @exception SWTException <ul> 3288 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3289 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3290 * </ul> 3291 */ 3292 public void setTopIndex (int index) { 3293 checkWidget(); 3294 if (!(0 <= index && index < itemCount)) return; 3295 auto path = OS.gtk_tree_model_get_path (modelHandle, _getItem (index).handle); 3296 OS.gtk_tree_view_scroll_to_cell (handle, path, null, true, 0f, 0f); 3297 if (OS.GTK_VERSION < OS.buildVERSION (2, 8, 0)) { 3298 /* 3299 * Bug in GTK. According to the documentation, gtk_tree_view_scroll_to_cell 3300 * should vertically scroll the cell to the top if use_align is true and row_align is 0. 3301 * However, prior to version 2.8 it does not scroll at all. The fix is to determine 3302 * the new location and use gtk_tree_view_scroll_to_point. 3303 * If the widget is a pinhead, calling gtk_tree_view_scroll_to_point 3304 * will have no effect. Therefore, it is still neccessary to call 3305 * gtk_tree_view_scroll_to_cell. 3306 */ 3307 OS.gtk_widget_realize (handle); 3308 GdkRectangle cellRect; 3309 OS.gtk_tree_view_get_cell_area (handle, path, null, &cellRect); 3310 int tx, ty; 3311 OS.gtk_tree_view_widget_to_tree_coords(handle, cellRect.x, cellRect.y, &tx, &ty); 3312 OS.gtk_tree_view_scroll_to_point (handle, -1, ty); 3313 } 3314 OS.gtk_tree_path_free (path); 3315 } 3316 3317 /** 3318 * Shows the column. If the column is already showing in the receiver, 3319 * this method simply returns. Otherwise, the columns are scrolled until 3320 * the column is visible. 3321 * 3322 * @param column the column to be shown 3323 * 3324 * @exception IllegalArgumentException <ul> 3325 * <li>ERROR_NULL_ARGUMENT - if the column is null</li> 3326 * <li>ERROR_INVALID_ARGUMENT - if the column has been disposed</li> 3327 * </ul> 3328 * @exception SWTException <ul> 3329 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3330 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3331 * </ul> 3332 * 3333 * @since 3.0 3334 */ 3335 public void showColumn (TableColumn column) { 3336 checkWidget (); 3337 if (column is null) error (SWT.ERROR_NULL_ARGUMENT); 3338 if (column.isDisposed()) error(SWT.ERROR_INVALID_ARGUMENT); 3339 if (column.parent !is this) return; 3340 /* 3341 * This code is intentionally commented. According to the 3342 * documentation, gtk_tree_view_scroll_to_cell should scroll the 3343 * minimum amount to show the column but instead it scrolls strangely. 3344 */ 3345 //OS.gtk_tree_view_scroll_to_cell (handle, 0, column.handle, false, 0, 0); 3346 OS.gtk_widget_realize (handle); 3347 GdkRectangle cellRect; 3348 OS.gtk_tree_view_get_cell_area (handle, null, column.handle, &cellRect); 3349 GdkRectangle visibleRect; 3350 OS.gtk_tree_view_get_visible_rect (handle, &visibleRect); 3351 if (cellRect.x < visibleRect.x) { 3352 OS.gtk_tree_view_scroll_to_point (handle, cellRect.x, -1); 3353 } else { 3354 int width = Math.min (visibleRect.width, cellRect.width); 3355 if (cellRect.x + width > visibleRect.x + visibleRect.width) { 3356 int tree_x = cellRect.x + width - visibleRect.width; 3357 OS.gtk_tree_view_scroll_to_point (handle, tree_x, -1); 3358 } 3359 } 3360 } 3361 3362 bool showFirstColumn () { 3363 /* 3364 * Bug in GTK. If no columns are visible, changing the selection 3365 * will fail. The fix is to temporarily make a column visible. 3366 */ 3367 int columnCount = Math.max (1, this.columnCount); 3368 for (int i=0; i<columnCount; i++) { 3369 auto column = OS.gtk_tree_view_get_column (handle, i); 3370 if (OS.gtk_tree_view_column_get_visible (column)) return false; 3371 } 3372 auto firstColumn = OS.gtk_tree_view_get_column (handle, 0); 3373 OS.gtk_tree_view_column_set_visible (firstColumn, true); 3374 return true; 3375 } 3376 3377 /** 3378 * Shows the item. If the item is already showing in the receiver, 3379 * this method simply returns. Otherwise, the items are scrolled until 3380 * the item is visible. 3381 * 3382 * @param item the item to be shown 3383 * 3384 * @exception IllegalArgumentException <ul> 3385 * <li>ERROR_NULL_ARGUMENT - if the item is null</li> 3386 * <li>ERROR_INVALID_ARGUMENT - if the item has been disposed</li> 3387 * </ul> 3388 * @exception SWTException <ul> 3389 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3390 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3391 * </ul> 3392 * 3393 * @see Table#showSelection() 3394 */ 3395 public void showItem (TableItem item) { 3396 checkWidget (); 3397 if (item is null) error (SWT.ERROR_NULL_ARGUMENT); 3398 if (item.isDisposed()) error (SWT.ERROR_INVALID_ARGUMENT); 3399 if (item.parent !is this) return; 3400 showItem (cast(GtkTreeIter*)item.handle); 3401 } 3402 3403 void showItem (GtkTreeIter* iter) { 3404 auto path = OS.gtk_tree_model_get_path (modelHandle, iter); 3405 /* 3406 * This code intentionally commented. 3407 * Bug in GTK. According to the documentation, gtk_tree_view_scroll_to_cell 3408 * should scroll the minimum amount to show the cell if use_align is false. 3409 * However, what actually happens is the cell is scrolled to the top. 3410 * The fix is to determine the new location and use gtk_tree_view_scroll_to_point. 3411 * If the widget is a pinhead, calling gtk_tree_view_scroll_to_point 3412 * will have no effect. Therefore, it is still neccessary to 3413 * call gtk_tree_view_scroll_to_cell. 3414 */ 3415 // OS.gtk_tree_view_scroll_to_cell (handle, path, 0, false, 0, 0); 3416 OS.gtk_widget_realize (handle); 3417 GdkRectangle visibleRect; 3418 OS.gtk_tree_view_get_visible_rect (handle, &visibleRect); 3419 GdkRectangle cellRect; 3420 OS.gtk_tree_view_get_cell_area (handle, path, null, &cellRect); 3421 int tx, ty; 3422 OS.gtk_tree_view_widget_to_tree_coords(handle, cellRect.x, cellRect.y, &tx, &ty); 3423 if (ty < visibleRect.y ) { 3424 OS.gtk_tree_view_scroll_to_cell (handle, path, null, true, 0f, 0f); 3425 OS.gtk_tree_view_scroll_to_point (handle, -1, ty); 3426 } else { 3427 int height = Math.min (visibleRect.height, cellRect.height); 3428 if (ty + height > visibleRect.y + visibleRect.height) { 3429 OS.gtk_tree_view_scroll_to_cell (handle, path, null, true, 1f, 0f); 3430 ty += cellRect.height - visibleRect.height; 3431 OS.gtk_tree_view_scroll_to_point (handle, -1, ty); 3432 } 3433 } 3434 OS.gtk_tree_path_free (path); 3435 } 3436 3437 /** 3438 * Shows the selection. If the selection is already showing in the receiver, 3439 * this method simply returns. Otherwise, the items are scrolled until 3440 * the selection is visible. 3441 * 3442 * @exception SWTException <ul> 3443 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 3444 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 3445 * </ul> 3446 * 3447 * @see Table#showItem(TableItem) 3448 */ 3449 public void showSelection () { 3450 checkWidget(); 3451 TableItem [] selection = getSelection (); 3452 if (selection.length is 0) return; 3453 TableItem item = selection [0]; 3454 showItem (cast(GtkTreeIter*)item.handle); 3455 } 3456 3457 override void treeSelectionProc (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, int[] selection, int length_) { 3458 if (selection !is null) { 3459 auto indices = OS.gtk_tree_path_get_indices (path); 3460 if (indices !is null) { 3461 selection [length_] = indices [0]; 3462 } 3463 } 3464 } 3465 3466 override void updateScrollBarValue (ScrollBar bar) { 3467 super.updateScrollBarValue (bar); 3468 /* 3469 * Bug in GTK. Scrolling changes the XWindow position 3470 * and makes the child widgets appear to scroll even 3471 * though when queried their position is unchanged. 3472 * The fix is to queue a resize event for each child to 3473 * force the position to be corrected. 3474 */ 3475 auto parentHandle = parentingHandle (); 3476 auto list = OS.gtk_container_get_children (parentHandle); 3477 if (list is null) return; 3478 auto temp = list; 3479 while (temp !is null) { 3480 auto widget = OS.g_list_data (temp); 3481 if (widget !is null) OS.gtk_widget_queue_resize (widget); 3482 temp = OS.g_list_next (temp); 3483 } 3484 OS.g_list_free (list); 3485 } 3486 3487 }