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