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.List; 14 15 16 import org.eclipse.swt.SWT; 17 import org.eclipse.swt.internal.gtk.OS; 18 import org.eclipse.swt.graphics.Point; 19 import org.eclipse.swt.graphics.Rectangle; 20 import org.eclipse.swt.events.SelectionListener; 21 import org.eclipse.swt.events.SelectionEvent; 22 import org.eclipse.swt.widgets.Scrollable; 23 import org.eclipse.swt.widgets.Composite; 24 import org.eclipse.swt.widgets.TypedListener; 25 import org.eclipse.swt.widgets.Display; 26 import java.lang.all; 27 28 /** 29 * Instances of this class represent a selectable user interface 30 * object that displays a list of strings and issues notification 31 * when a string is selected. A list may be single or multi select. 32 * <p> 33 * <dl> 34 * <dt><b>Styles:</b></dt> 35 * <dd>SINGLE, MULTI</dd> 36 * <dt><b>Events:</b></dt> 37 * <dd>Selection, DefaultSelection</dd> 38 * </dl> 39 * <p> 40 * Note: Only one of SINGLE and MULTI may be specified. 41 * </p><p> 42 * IMPORTANT: This class is <em>not</em> intended to be subclassed. 43 * </p> 44 * 45 * @see <a href="http://www.eclipse.org/swt/snippets/#list">List snippets</a> 46 * @see <a href="http://www.eclipse.org/swt/examples.php">SWT Example: ControlExample</a> 47 * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> 48 */ 49 public class List : Scrollable { 50 51 alias Scrollable.computeSize computeSize; 52 alias Scrollable.dragDetect dragDetect; 53 alias Scrollable.setBackgroundColor setBackgroundColor; 54 alias Scrollable.setBounds setBounds; 55 56 GtkWidget* modelHandle; 57 58 static const int TEXT_COLUMN = 0; 59 CallbackData treeSelectionProcCallbackData; 60 61 /** 62 * Constructs a new instance of this class given its parent 63 * and a style value describing its behavior and appearance. 64 * <p> 65 * The style value is either one of the style constants defined in 66 * class <code>SWT</code> which is applicable to instances of this 67 * class, or must be built by <em>bitwise OR</em>'ing together 68 * (that is, using the <code>int</code> "|" operator) two or more 69 * of those <code>SWT</code> style constants. The class description 70 * lists the style constants that are applicable to the class. 71 * Style bits are also inherited from superclasses. 72 * </p> 73 * 74 * @param parent a composite control which will be the parent of the new instance (cannot be null) 75 * @param style the style of control to construct 76 * 77 * @exception IllegalArgumentException <ul> 78 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> 79 * </ul> 80 * @exception SWTException <ul> 81 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> 82 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> 83 * </ul> 84 * 85 * @see SWT#SINGLE 86 * @see SWT#MULTI 87 * @see Widget#checkSubclass 88 * @see Widget#getStyle 89 */ 90 public this (Composite parent, int style) { 91 super (parent, checkStyle (style)); 92 } 93 94 /** 95 * Adds the argument to the end of the receiver's list. 96 * 97 * @param string the new item 98 * 99 * @exception SWTException <ul> 100 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 101 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 102 * </ul> 103 * 104 * @see #add(String,int) 105 */ 106 public void add (String string) { 107 checkWidget(); 108 // SWT extension: allow null for zero length string 109 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 110 char* buffer = string.toStringzValidPtr(); 111 GtkTreeIter iter; 112 OS.gtk_list_store_append (cast(GtkListStore*)modelHandle, &iter); 113 OS.gtk_list_store_set1 (cast(GtkListStore*)modelHandle, &iter, TEXT_COLUMN, buffer); 114 } 115 116 /** 117 * Adds the argument to the receiver's list at the given 118 * zero-relative index. 119 * <p> 120 * Note: To add an item at the end of the list, use the 121 * result of calling <code>getItemCount()</code> as the 122 * index or use <code>add(String)</code>. 123 * </p> 124 * 125 * @param string the new item 126 * @param index the index for the item 127 * 128 * @exception IllegalArgumentException <ul> 129 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list (inclusive)</li> 130 * </ul> 131 * @exception SWTException <ul> 132 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 133 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 134 * </ul> 135 * 136 * @see #add(String) 137 */ 138 public void add (String string, int index) { 139 checkWidget(); 140 // SWT extension: allow null for zero length string 141 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 142 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 143 if (!(0 <= index && index <= count)) { 144 error (SWT.ERROR_INVALID_RANGE); 145 } 146 char* buffer = string.toStringzValidPtr(); 147 GtkTreeIter iter; 148 /* 149 * Feature in GTK. It is much faster to append to a list store 150 * than to insert at the end using gtk_list_store_insert(). 151 */ 152 if (index is count) { 153 OS.gtk_list_store_append (cast(GtkListStore*)modelHandle, &iter); 154 } else { 155 OS.gtk_list_store_insert (cast(GtkListStore*)modelHandle, &iter, index); 156 } 157 OS.gtk_list_store_set1 (cast(GtkListStore*)modelHandle, &iter, TEXT_COLUMN, buffer); 158 } 159 160 /** 161 * Adds the listener to the collection of listeners who will 162 * be notified when the user changes the receiver's selection, by sending 163 * it one of the messages defined in the <code>SelectionListener</code> 164 * interface. 165 * <p> 166 * <code>widgetSelected</code> is called when the selection changes. 167 * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. 168 * </p> 169 * 170 * @param listener the listener which should be notified when the user changes the receiver's selection 171 * 172 * @exception IllegalArgumentException <ul> 173 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 174 * </ul> 175 * @exception SWTException <ul> 176 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 177 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 178 * </ul> 179 * 180 * @see SelectionListener 181 * @see #removeSelectionListener 182 * @see SelectionEvent 183 */ 184 public void addSelectionListener(SelectionListener listener) { 185 checkWidget(); 186 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 187 TypedListener typedListener = new TypedListener (listener); 188 addListener (SWT.Selection,typedListener); 189 addListener (SWT.DefaultSelection,typedListener); 190 } 191 192 static int checkStyle (int style) { 193 return checkBits (style, SWT.SINGLE, SWT.MULTI, 0, 0, 0, 0); 194 } 195 196 override void createHandle (int index) { 197 state |= HANDLE; 198 fixedHandle = cast(GtkWidget*)OS.g_object_new (display.gtk_fixed_get_type (), null); 199 if (fixedHandle is null) error (SWT.ERROR_NO_HANDLES); 200 OS.gtk_fixed_set_has_window (cast(GtkFixed*)fixedHandle, true); 201 scrolledHandle = cast(GtkWidget*)OS.gtk_scrolled_window_new (null, null); 202 if (scrolledHandle is null) error (SWT.ERROR_NO_HANDLES); 203 /* 204 * Columns: 205 * 0 - text 206 */ 207 size_t[] types = [OS.G_TYPE_STRING ()]; 208 modelHandle = cast(GtkWidget*)OS.gtk_list_store_newv (cast(int)/*64bit*/types.length, types.ptr); 209 if (modelHandle is null) error (SWT.ERROR_NO_HANDLES); 210 handle = OS.gtk_tree_view_new_with_model (modelHandle); 211 if (handle is null) error (SWT.ERROR_NO_HANDLES); 212 auto textRenderer = OS.gtk_cell_renderer_text_new (); 213 if (textRenderer is null) error (SWT.ERROR_NO_HANDLES); 214 auto columnHandle = OS.gtk_tree_view_column_new (); 215 if (columnHandle is null) error (SWT.ERROR_NO_HANDLES); 216 OS.gtk_tree_view_column_pack_start (cast(GtkTreeViewColumn*)columnHandle, textRenderer, true); 217 OS.gtk_tree_view_column_add_attribute (cast(GtkTreeViewColumn*)columnHandle, textRenderer, OS.text.ptr, TEXT_COLUMN); 218 OS.gtk_tree_view_insert_column (cast(GtkTreeView*)handle, columnHandle, index); 219 OS.gtk_container_add (cast(GtkContainer*)fixedHandle, scrolledHandle); 220 OS.gtk_container_add (cast(GtkContainer*)scrolledHandle, handle); 221 222 int mode = (style & SWT.MULTI) !is 0 ? OS.GTK_SELECTION_MULTIPLE : OS.GTK_SELECTION_BROWSE; 223 auto selectionHandle = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 224 OS.gtk_tree_selection_set_mode (selectionHandle, mode); 225 OS.gtk_tree_view_set_headers_visible (cast(GtkTreeView*)handle, false); 226 int hsp = (style & SWT.H_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER; 227 int vsp = (style & SWT.V_SCROLL) !is 0 ? OS.GTK_POLICY_AUTOMATIC : OS.GTK_POLICY_NEVER; 228 OS.gtk_scrolled_window_set_policy (cast(GtkScrolledWindow*)scrolledHandle, hsp, vsp); 229 if ((style & SWT.BORDER) !is 0) OS.gtk_scrolled_window_set_shadow_type (cast(GtkScrolledWindow*)scrolledHandle, OS.GTK_SHADOW_ETCHED_IN); 230 /* 231 * Bug in GTK. When a treeview is the child of an override shell, 232 * and if the user has ever invokes the interactive search field, 233 * and the treeview is disposed on a focus out event, it segment 234 * faults. The fix is to disable the search field in an override 235 * shell. 236 */ 237 if ((getShell ().style & SWT.ON_TOP) !is 0) { 238 /* 239 * Bug in GTK. Until GTK 2.6.5, calling gtk_tree_view_set_enable_search(FALSE) 240 * would prevent the user from being able to type in text to search the tree. 241 * After 2.6.5, GTK introduced Ctrl+F as being the key binding for interactive 242 * search. This meant that even if FALSE was passed to enable_search, the user 243 * can still bring up the search pop up using the keybinding. GTK also introduced 244 * the notion of passing a -1 to gtk_set_search_column to disable searching 245 * (including the search key binding). The fix is to use the right calls 246 * for the right version. 247 */ 248 if (OS.GTK_VERSION >= OS.buildVERSION (2, 6, 5)) { 249 OS.gtk_tree_view_set_search_column (cast(GtkTreeView*)handle, -1); 250 } else { 251 OS.gtk_tree_view_set_enable_search (cast(GtkTreeView*)handle, false); 252 } 253 } 254 } 255 256 public override Point computeSize (int wHint, int hHint, bool changed) { 257 checkWidget (); 258 if (wHint !is SWT.DEFAULT && wHint < 0) wHint = 0; 259 if (hHint !is SWT.DEFAULT && hHint < 0) hHint = 0; 260 Point size = computeNativeSize (handle, wHint, hHint, changed); 261 Rectangle trim = computeTrim (0, 0, size.x, size.y); 262 size.x = trim.width; 263 size.y = trim.height; 264 return size; 265 } 266 267 override void deregister() { 268 super.deregister (); 269 display.removeWidget (cast(GtkWidget*)OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle)); 270 } 271 272 /** 273 * Deselects the item at the given zero-relative index in the receiver. 274 * If the item at the index was already deselected, it remains 275 * deselected. Indices that are out of range are ignored. 276 * 277 * @param index the index of the item to deselect 278 * 279 * @exception SWTException <ul> 280 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 281 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 282 * </ul> 283 */ 284 public void deselect (int index) { 285 checkWidget(); 286 if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null))) return; 287 GtkTreeIter iter; 288 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 289 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 290 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 291 OS.gtk_tree_selection_unselect_iter (selection, &iter); 292 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 293 } 294 295 /** 296 * Deselects the items at the given zero-relative indices in the receiver. 297 * If the item at the given zero-relative index in the receiver 298 * is selected, it is deselected. If the item at the index 299 * was not selected, it remains deselected. The range of the 300 * indices is inclusive. Indices that are out of range are ignored. 301 * 302 * @param start the start index of the items to deselect 303 * @param end the end index of the items to deselect 304 * 305 * @exception SWTException <ul> 306 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 307 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 308 * </ul> 309 */ 310 public void deselect (int start, int end) { 311 checkWidget(); 312 if (start < 0 && end < 0) return; 313 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 314 if (start >= count && end >= count) return; 315 start = Math.min (count - 1, Math.max (0, start)); 316 end = Math.min (count - 1, Math.max (0, end)); 317 GtkTreeIter iter; 318 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 319 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 320 for (int index=start; index<=end; index++) { 321 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 322 OS.gtk_tree_selection_unselect_iter (selection, &iter); 323 } 324 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 325 } 326 327 /** 328 * Deselects the items at the given zero-relative indices in the receiver. 329 * If the item at the given zero-relative index in the receiver 330 * is selected, it is deselected. If the item at the index 331 * was not selected, it remains deselected. Indices that are out 332 * of range and duplicate indices are ignored. 333 * 334 * @param indices the array of indices for the items to deselect 335 * 336 * @exception SWTException <ul> 337 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 338 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 339 * </ul> 340 */ 341 public void deselect (int [] indices) { 342 checkWidget(); 343 // SWT extension: allow null for zero length string 344 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 345 GtkTreeIter iter; 346 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 347 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 348 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 349 for (int i=0; i<indices.length; i++) { 350 int index = indices [i]; 351 if (index < 0 || index > count - 1) continue; 352 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 353 OS.gtk_tree_selection_unselect_iter (selection, &iter); 354 } 355 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 356 } 357 358 /** 359 * Deselects all selected items in the receiver. 360 * 361 * @exception SWTException <ul> 362 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 363 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 364 * </ul> 365 */ 366 public void deselectAll () { 367 checkWidget(); 368 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 369 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 370 OS.gtk_tree_selection_unselect_all (selection); 371 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 372 } 373 374 override bool dragDetect (int x, int y, bool filter, bool* consume) { 375 bool selected = false; 376 if (filter) { 377 GtkTreePath* path; 378 if (OS.gtk_tree_view_get_path_at_pos (cast(GtkTreeView*)handle, x, y, &path, null, null, null)) { 379 if (path !is null) { 380 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 381 if (OS.gtk_tree_selection_path_is_selected (selection, path)) selected = true; 382 OS.gtk_tree_path_free (path); 383 } 384 } else { 385 return false; 386 } 387 } 388 bool dragDetect = super.dragDetect (x, y, filter, consume); 389 if (dragDetect && selected && consume !is null) *consume = true; 390 return dragDetect; 391 } 392 393 override GdkDrawable* eventWindow () { 394 return paintWindow (); 395 } 396 397 override GdkColor* getBackgroundColor () { 398 return getBaseColor (); 399 } 400 401 /** 402 * Returns the zero-relative index of the item which currently 403 * has the focus in the receiver, or -1 if no item has focus. 404 * 405 * @return the index of the selected item 406 * 407 * @exception SWTException <ul> 408 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 409 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 410 * </ul> 411 */ 412 public int getFocusIndex () { 413 checkWidget(); 414 GtkTreePath * path; 415 OS.gtk_tree_view_get_cursor (cast(GtkTreeView*)handle, &path, null); 416 if (path is null) return -1; 417 int* indices = OS.gtk_tree_path_get_indices (path); 418 int index; 419 if (indices !is null) index = indices[0]; 420 OS.gtk_tree_path_free (path); 421 return index; 422 } 423 424 override 425 GdkColor* getForegroundColor () { 426 return getTextColor (); 427 } 428 429 /** 430 * Returns the item at the given, zero-relative index in the 431 * receiver. Throws an exception if the index is out of range. 432 * 433 * @param index the index of the item to return 434 * @return the item at the given index 435 * 436 * @exception IllegalArgumentException <ul> 437 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 438 * </ul> 439 * @exception SWTException <ul> 440 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 441 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 442 * </ul> 443 */ 444 public String getItem (int index) { 445 checkWidget(); 446 if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null))) { 447 error (SWT.ERROR_INVALID_RANGE); 448 } 449 char* ptr; 450 GtkTreeIter iter; 451 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 452 OS.gtk_tree_model_get1 (cast(GtkTreeStore*)modelHandle, &iter, 0, cast(void**)&ptr ); 453 if (ptr is null) return null; 454 String res = fromStringz( ptr )._idup(); 455 OS.g_free (ptr); 456 return res; 457 } 458 459 /** 460 * Returns the number of items contained in the receiver. 461 * 462 * @return the number of items 463 * 464 * @exception SWTException <ul> 465 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 466 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 467 * </ul> 468 */ 469 public int getItemCount () { 470 checkWidget(); 471 return OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 472 } 473 474 /** 475 * Returns the height of the area which would be used to 476 * display <em>one</em> of the items in the list. 477 * 478 * @return the height of one item 479 * 480 * @exception SWTException <ul> 481 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 482 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 483 * </ul> 484 */ 485 public int getItemHeight () { 486 checkWidget(); 487 int itemCount = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 488 auto column = OS.gtk_tree_view_get_column (cast(GtkTreeView*)handle, 0); 489 if (itemCount is 0) { 490 int w, h; 491 OS.gtk_tree_view_column_cell_get_size (cast(GtkTreeViewColumn*)column, null, null, null, &w, &h); 492 return h; 493 } else { 494 GtkTreeIter iter; 495 OS.gtk_tree_model_get_iter_first (cast(GtkTreeStore*)modelHandle, &iter); 496 OS.gtk_tree_view_column_cell_set_cell_data (cast(GtkTreeViewColumn*)column, modelHandle, &iter, false, false); 497 int w, h; 498 OS.gtk_tree_view_column_cell_get_size (cast(GtkTreeViewColumn*)column, null, null, null, &w, &h); 499 return h; 500 } 501 } 502 503 /** 504 * Returns a (possibly empty) array of <code>String</code>s which 505 * are the items in the receiver. 506 * <p> 507 * Note: This is not the actual structure used by the receiver 508 * to maintain its list of items, so modifying the array will 509 * not affect the receiver. 510 * </p> 511 * 512 * @return the items in the receiver's list 513 * 514 * @exception SWTException <ul> 515 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 516 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 517 * </ul> 518 */ 519 public String [] getItems () { 520 checkWidget(); 521 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 522 char* ptr; 523 String [] result = new String[]( count ); 524 GtkTreeIter iter; 525 for (int index=0; index<count; index++) { 526 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 527 OS.gtk_tree_model_get1 (cast(GtkTreeStore*)modelHandle, &iter, 0, cast(void**)&ptr); 528 if (ptr !is null) { 529 String res = fromStringz( ptr )._idup(); 530 OS.g_free (ptr); 531 result [index] = res; 532 } 533 } 534 return result; 535 } 536 537 /** 538 * Returns an array of <code>String</code>s that are currently 539 * selected in the receiver. The order of the items is unspecified. 540 * An empty array indicates that no items are selected. 541 * <p> 542 * Note: This is not the actual structure used by the receiver 543 * to maintain its selection, so modifying the array will 544 * not affect the receiver. 545 * </p> 546 * @return an array representing the selection 547 * 548 * @exception SWTException <ul> 549 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 550 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 551 * </ul> 552 */ 553 public String [] getSelection () { 554 checkWidget(); 555 int [] indices = getSelectionIndices (); 556 String [] result = new String[](indices.length); 557 for (int i=0; i<indices.length; i++) { 558 result [i] = getItem (indices [i]); 559 } 560 return result; 561 } 562 563 /** 564 * Returns the number of selected items contained in the receiver. 565 * 566 * @return the number of selected items 567 * 568 * @exception SWTException <ul> 569 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 570 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 571 * </ul> 572 */ 573 public int getSelectionCount () { 574 checkWidget(); 575 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 576 if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) { 577 display.treeSelectionLength = 0; 578 display.treeSelection = null; 579 display.doTreeSelectionProcConnect( &treeSelectionProcCallbackData, handle, selection ); 580 return display.treeSelectionLength; 581 } 582 return OS.gtk_tree_selection_count_selected_rows (selection); 583 } 584 585 /** 586 * Returns the zero-relative index of the item which is currently 587 * selected in the receiver, or -1 if no item is selected. 588 * 589 * @return the index of the selected item or -1 590 * 591 * @exception SWTException <ul> 592 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 593 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 594 * </ul> 595 */ 596 public int getSelectionIndex () { 597 checkWidget(); 598 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 599 if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) { 600 int itemCount = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 601 display.treeSelectionLength = 0; 602 display.treeSelection = new int [itemCount]; 603 display.doTreeSelectionProcConnect( &treeSelectionProcCallbackData, handle, selection ); 604 if (display.treeSelectionLength is 0) return -1; 605 return display.treeSelection [0]; 606 } 607 /* 608 * Bug in GTK. gtk_tree_selection_get_selected_rows() segmentation faults 609 * in versions smaller than 2.2.4 if the model is NULL. The fix is 610 * to give a valid pointer instead. 611 */ 612 int dummy; 613 int* model = OS.GTK_VERSION < OS.buildVERSION (2, 2, 4) ? &dummy : null; 614 auto list = OS.gtk_tree_selection_get_selected_rows (selection, cast(void**)model); 615 if (list !is null) { 616 int count = OS.g_list_length (list); 617 int index; 618 for (int i=0; i<count; i++) { 619 auto data = OS.g_list_nth_data (list, i); 620 auto indices = OS.gtk_tree_path_get_indices (data); 621 if (indices !is null) { 622 index = indices[0]; 623 break; 624 } 625 } 626 OS.g_list_free (list); 627 return index; 628 } 629 return -1; 630 } 631 632 /** 633 * Returns the zero-relative indices of the items which are currently 634 * selected in the receiver. The order of the indices is unspecified. 635 * The array is empty if no items are selected. 636 * <p> 637 * Note: This is not the actual structure used by the receiver 638 * to maintain its selection, so modifying the array will 639 * not affect the receiver. 640 * </p> 641 * @return the array of indices of the selected items 642 * 643 * @exception SWTException <ul> 644 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 645 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 646 * </ul> 647 */ 648 public int [] getSelectionIndices () { 649 checkWidget(); 650 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 651 if (OS.GTK_VERSION < OS.buildVERSION (2, 2, 0)) { 652 int itemCount = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 653 display.treeSelectionLength = 0; 654 display.treeSelection = new int [itemCount]; 655 display.doTreeSelectionProcConnect( &treeSelectionProcCallbackData, handle, selection ); 656 int [] result = new int [display.treeSelectionLength]; 657 for (int i = 0; i < display.treeSelectionLength; i++) { 658 result[i] = display.treeSelection[i]; 659 } 660 return result; 661 } 662 /* 663 * Bug in GTK. gtk_tree_selection_get_selected_rows() segmentation faults 664 * in versions smaller than 2.2.4 if the model is NULL. The fix is 665 * to give a valid pointer instead. 666 */ 667 int dummy; 668 int* model = OS.GTK_VERSION < OS.buildVERSION (2, 2, 4) ? &dummy : null; 669 auto list = OS.gtk_tree_selection_get_selected_rows (selection, cast(void**)model); 670 if (list !is null) { 671 int count = OS.g_list_length (list); 672 int [] treeSelection = new int [count]; 673 int len = 0; 674 for (int i=0; i<count; i++) { 675 auto data = OS.g_list_nth_data (list, i); 676 auto indices = OS.gtk_tree_path_get_indices (data); 677 if (indices !is null) { 678 treeSelection [len] = indices [0]; 679 len++; 680 } 681 } 682 OS.g_list_free (list); 683 int [] result = treeSelection[0..len].dup; 684 return result; 685 } 686 return [0]; 687 } 688 689 /** 690 * Returns the zero-relative index of the item which is currently 691 * at the top of the receiver. This index can change when items are 692 * scrolled or new items are added or removed. 693 * 694 * @return the index of the top item 695 * 696 * @exception SWTException <ul> 697 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 698 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 699 * </ul> 700 */ 701 public int getTopIndex () { 702 checkWidget(); 703 GtkTreePath* path; 704 OS.gtk_widget_realize (handle); 705 if (!OS.gtk_tree_view_get_path_at_pos (cast(GtkTreeView*)handle, 1, 1, &path, null, null, null)) return 0; 706 if (path is null) return 0; 707 auto indices = OS.gtk_tree_path_get_indices (path); 708 int index; 709 if (indices !is null) index = indices[0]; 710 OS.gtk_tree_path_free (path); 711 return index; 712 } 713 714 override int gtk_changed (GtkWidget* widget) { 715 postEvent (SWT.Selection); 716 return 0; 717 } 718 719 override int gtk_button_press_event (GtkWidget* widget, GdkEventButton* gdkEvent) { 720 auto result = super.gtk_button_press_event (widget, gdkEvent); 721 if (result !is 0) return result; 722 /* 723 * Feature in GTK. In a multi-select list view, when multiple items are already 724 * selected, the selection state of the item is toggled and the previous selection 725 * is cleared. This is not the desired behaviour when bringing up a popup menu. 726 * Also, when an item is reselected with the right button, the tree view issues 727 * an unwanted selection event. The workaround is to detect that case and not 728 * run the default handler when the item is already part of the current selection. 729 */ 730 int button = gdkEvent.button; 731 if (button is 3 && gdkEvent.type is OS.GDK_BUTTON_PRESS) { 732 GtkTreePath* path; 733 if (OS.gtk_tree_view_get_path_at_pos (cast(GtkTreeView*)handle, cast(int)gdkEvent.x, cast(int)gdkEvent.y, &path, null, null, null)) { 734 if (path !is null) { 735 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 736 if (OS.gtk_tree_selection_path_is_selected (selection, path)) result = 1; 737 OS.gtk_tree_path_free (path); 738 } 739 } 740 } 741 742 /* 743 * Feature in GTK. When the user clicks in a single selection GtkTreeView 744 * and there are no selected items, the first item is selected automatically 745 * before the click is processed, causing two selection events. The is fix 746 * is the set the cursor item to be same as the clicked item to stop the 747 * widget from automatically selecting the first item. 748 */ 749 if ((style & SWT.SINGLE) !is 0 && getSelectionCount () is 0) { 750 GtkTreePath* path; 751 if (OS.gtk_tree_view_get_path_at_pos (cast(GtkTreeView*)handle, cast(int)gdkEvent.x, cast(int)gdkEvent.y, &path, null, null, null)) { 752 if (path !is null) { 753 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 754 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 755 OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false); 756 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 757 OS.gtk_tree_path_free (path); 758 } 759 } 760 } 761 /* 762 * Bug in GTK. GTK segments fault, if the GtkTreeView widget is 763 * not in focus and all items in the widget are disposed before 764 * it finishes processing a button press. The fix is to give 765 * focus to the widget before it starts processing the event. 766 */ 767 if (!OS.GTK_WIDGET_HAS_FOCUS (handle)) { 768 OS.gtk_widget_grab_focus (handle); 769 } 770 return result; 771 } 772 773 override int gtk_key_press_event (GtkWidget* widget, GdkEventKey* keyEvent) { 774 auto result = super.gtk_key_press_event (widget, keyEvent); 775 if (result !is 0) return result; 776 if (OS.GTK_VERSION < OS.buildVERSION (2, 2 ,0)) { 777 /* 778 * Feature in GTK 2.0.x. When an item is default selected using 779 * the return key, GTK does not issue notification. The fix is 780 * to issue this notification when the return key is pressed. 781 */ 782 int key = keyEvent.keyval; 783 switch (key) { 784 case OS.GDK_Return: 785 case OS.GDK_KP_Enter: { 786 postEvent (SWT.DefaultSelection); 787 break; 788 } 789 default: 790 } 791 } 792 return result; 793 } 794 795 override int gtk_popup_menu (GtkWidget* widget) { 796 auto result = super.gtk_popup_menu (widget); 797 /* 798 * Bug in GTK. The context menu for the typeahead in GtkTreeViewer 799 * opens in the bottom right corner of the screen when Shift+F10 800 * is pressed and the typeahead window was not visible. The fix is 801 * to prevent the context menu from opening by stopping the default 802 * handler. 803 * 804 * NOTE: The bug only happens in GTK 2.6.5 and lower. 805 */ 806 return OS.GTK_VERSION < OS.buildVERSION (2, 6, 5) ? 1 : result; 807 } 808 809 override void gtk_row_activated (GtkTreeView* tree, GtkTreePath* path, GtkTreeViewColumn* column){ 810 postEvent (SWT.DefaultSelection); 811 } 812 813 override void hookEvents () { 814 super.hookEvents(); 815 auto selection = OS.gtk_tree_view_get_selection(cast(GtkTreeView*)handle); 816 OS.g_signal_connect_closure (selection, OS.changed.ptr, display.closures [CHANGED], false); 817 OS.g_signal_connect_closure (handle, OS.row_activated.ptr, display.closures [ROW_ACTIVATED], false); 818 } 819 820 /** 821 * Gets the index of an item. 822 * <p> 823 * The list is searched starting at 0 until an 824 * item is found that is equal to the search item. 825 * If no item is found, -1 is returned. Indexing 826 * is zero based. 827 * 828 * @param string the search item 829 * @return the index of the item 830 * 831 * @exception SWTException <ul> 832 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 833 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 834 * </ul> 835 */ 836 public int indexOf (String string) { 837 checkWidget(); 838 return indexOf (string, 0); 839 } 840 841 /** 842 * Searches the receiver's list starting at the given, 843 * zero-relative index until an item is found that is equal 844 * to the argument, and returns the index of that item. If 845 * no item is found or the starting index is out of range, 846 * returns -1. 847 * 848 * @param string the search item 849 * @param start the zero-relative index at which to start the search 850 * @return the index of the item 851 * 852 * @exception SWTException <ul> 853 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 854 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 855 * </ul> 856 */ 857 public int indexOf (String string, int start) { 858 checkWidget(); 859 // SWT extension: allow null for zero length string 860 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 861 String [] items = getItems (); 862 for (int i=start; i<items.length; i++) { 863 if (items [i].equals (string)) return i; 864 } 865 return -1; 866 } 867 868 /** 869 * Returns <code>true</code> if the item is selected, 870 * and <code>false</code> otherwise. Indices out of 871 * range are ignored. 872 * 873 * @param index the index of the item 874 * @return the selection state of the item at the index 875 * 876 * @exception SWTException <ul> 877 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 878 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 879 * </ul> 880 */ 881 public bool isSelected (int index) { 882 checkWidget(); 883 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 884 char* buffer = toStringz(Integer.toString(index)); 885 auto path = OS.gtk_tree_path_new_from_string (buffer); 886 bool answer = cast(bool)OS.gtk_tree_selection_path_is_selected (selection, path); 887 OS.gtk_tree_path_free (path); 888 return answer; 889 } 890 891 override GdkDrawable* paintWindow () { 892 OS.gtk_widget_realize (handle); 893 return OS.gtk_tree_view_get_bin_window (cast(GtkTreeView*)handle); 894 } 895 896 override void register () { 897 super.register (); 898 display.addWidget (cast(GtkWidget*)OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle), this); 899 } 900 901 override void releaseWidget () { 902 super.releaseWidget (); 903 if (modelHandle !is null) OS.g_object_unref (modelHandle); 904 modelHandle = null; 905 } 906 907 /** 908 * Removes the item from the receiver at the given 909 * zero-relative index. 910 * 911 * @param index the index for the item 912 * 913 * @exception IllegalArgumentException <ul> 914 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 915 * </ul> 916 * @exception SWTException <ul> 917 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 918 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 919 * </ul> 920 */ 921 public void remove (int index) { 922 checkWidget(); 923 if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null))) { 924 error (SWT.ERROR_INVALID_RANGE); 925 } 926 GtkTreeIter iter; 927 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 928 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 929 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 930 OS.gtk_list_store_remove (cast(GtkListStore*)modelHandle, &iter); 931 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 932 } 933 934 /** 935 * Removes the items from the receiver which are 936 * between the given zero-relative start and end 937 * indices (inclusive). 938 * 939 * @param start the start of the range 940 * @param end the end of the range 941 * 942 * @exception IllegalArgumentException <ul> 943 * <li>ERROR_INVALID_RANGE - if either the start or end are not between 0 and the number of elements in the list minus 1 (inclusive)</li> 944 * </ul> 945 * @exception SWTException <ul> 946 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 947 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 948 * </ul> 949 */ 950 public void remove (int start, int end) { 951 checkWidget(); 952 if (start > end) return; 953 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 954 if (!(0 <= start && start <= end && end < count)) { 955 error (SWT.ERROR_INVALID_RANGE); 956 } 957 GtkTreeIter iter; 958 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 959 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 960 for (int index=end; index>=start; index--) { 961 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 962 OS.gtk_list_store_remove (cast(GtkListStore*)modelHandle, &iter); 963 } 964 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 965 } 966 967 /** 968 * Searches the receiver's list starting at the first item 969 * until an item is found that is equal to the argument, 970 * and removes that item from the list. 971 * 972 * @param string the item to remove 973 * 974 * @exception IllegalArgumentException <ul> 975 * <li>ERROR_INVALID_ARGUMENT - if the string is not found in the list</li> 976 * </ul> 977 * @exception SWTException <ul> 978 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 979 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 980 * </ul> 981 */ 982 public void remove (String string) { 983 checkWidget(); 984 // SWT extension: allow null for zero length string 985 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 986 int index = indexOf (string, 0); 987 if (index is -1) error (SWT.ERROR_INVALID_ARGUMENT); 988 remove (index); 989 } 990 991 /** 992 * Removes the items from the receiver at the given 993 * zero-relative indices. 994 * 995 * @param indices the array of indices of the items 996 * 997 * @exception IllegalArgumentException <ul> 998 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 999 * </ul> 1000 * @exception SWTException <ul> 1001 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1002 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1003 * </ul> 1004 */ 1005 public void remove (int [] indices) { 1006 checkWidget(); 1007 // SWT extension: allow null for zero length string 1008 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 1009 if (indices.length is 0) return; 1010 int [] newIndices = new int []( indices.length ); 1011 System.arraycopy (indices, 0, newIndices, 0, indices.length); 1012 sort (newIndices); 1013 int start = newIndices [newIndices.length - 1], end = newIndices [0]; 1014 int count = getItemCount(); 1015 if (!(0 <= start && start <= end && end < count)) { 1016 error (SWT.ERROR_INVALID_RANGE); 1017 } 1018 GtkTreeIter iter; 1019 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 1020 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1021 int last = -1; 1022 for (int i=0; i<newIndices.length; i++) { 1023 int index = newIndices [i]; 1024 if (index !is last) { 1025 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 1026 OS.gtk_list_store_remove (cast(GtkListStore*)modelHandle, &iter); 1027 last = index; 1028 } 1029 } 1030 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1031 } 1032 1033 /** 1034 * Removes all of the items from the receiver. 1035 * <p> 1036 * @exception SWTException <ul> 1037 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1038 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1039 * </ul> 1040 */ 1041 public void removeAll () { 1042 checkWidget(); 1043 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 1044 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1045 OS.gtk_list_store_clear (cast(GtkListStore*)modelHandle); 1046 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1047 } 1048 1049 /** 1050 * Removes the listener from the collection of listeners who will 1051 * be notified when the user changes the receiver's selection. 1052 * 1053 * @param listener the listener which should no longer be notified 1054 * 1055 * @exception IllegalArgumentException <ul> 1056 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> 1057 * </ul> 1058 * @exception SWTException <ul> 1059 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1060 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1061 * </ul> 1062 * 1063 * @see SelectionListener 1064 * @see #addSelectionListener 1065 */ 1066 public void removeSelectionListener(SelectionListener listener) { 1067 checkWidget(); 1068 if (listener is null) error (SWT.ERROR_NULL_ARGUMENT); 1069 if (eventTable is null) return; 1070 eventTable.unhook (SWT.Selection, listener); 1071 eventTable.unhook (SWT.DefaultSelection,listener); 1072 } 1073 1074 /** 1075 * Selects the item at the given zero-relative index in the receiver's 1076 * list. If the item at the index was already selected, it remains 1077 * selected. Indices that are out of range are ignored. 1078 * 1079 * @param index the index of the item to select 1080 * 1081 * @exception SWTException <ul> 1082 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1083 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1084 * </ul> 1085 */ 1086 public void select (int index) { 1087 checkWidget(); 1088 if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null))) return; 1089 GtkTreeIter iter; 1090 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 1091 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1092 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 1093 OS.gtk_tree_selection_select_iter (selection, &iter); 1094 if ((style & SWT.SINGLE) !is 0) { 1095 auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter); 1096 OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false); 1097 OS.gtk_tree_path_free (path); 1098 } 1099 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1100 } 1101 1102 /** 1103 * Selects the items in the range specified by the given zero-relative 1104 * indices in the receiver. The range of indices is inclusive. 1105 * The current selection is not cleared before the new items are selected. 1106 * <p> 1107 * If an item in the given range is not selected, it is selected. 1108 * If an item in the given range was already selected, it remains selected. 1109 * Indices that are out of range are ignored and no items will be selected 1110 * if start is greater than end. 1111 * If the receiver is single-select and there is more than one item in the 1112 * given range, then all indices are ignored. 1113 * 1114 * @param start the start of the range 1115 * @param end the end of the range 1116 * 1117 * @exception SWTException <ul> 1118 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1119 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1120 * </ul> 1121 * 1122 * @see List#setSelection(int,int) 1123 */ 1124 public void select (int start, int end) { 1125 checkWidget (); 1126 if (end < 0 || start > end || ((style & SWT.SINGLE) !is 0 && start !is end)) return; 1127 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 1128 if (count is 0 || start >= count) return; 1129 start = Math.max (0, start); 1130 end = Math.min (end, count - 1); 1131 GtkTreeIter iter; 1132 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 1133 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1134 for (int index=start; index<=end; index++) { 1135 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 1136 OS.gtk_tree_selection_select_iter (selection, &iter); 1137 if ((style & SWT.SINGLE) !is 0) { 1138 auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter); 1139 OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false); 1140 OS.gtk_tree_path_free (path); 1141 } 1142 } 1143 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1144 } 1145 1146 /** 1147 * Selects the items at the given zero-relative indices in the receiver. 1148 * The current selection is not cleared before the new items are selected. 1149 * <p> 1150 * If the item at a given index is not selected, it is selected. 1151 * If the item at a given index was already selected, it remains selected. 1152 * Indices that are out of range and duplicate indices are ignored. 1153 * If the receiver is single-select and multiple indices are specified, 1154 * then all indices are ignored. 1155 * 1156 * @param indices the array of indices for the items to select 1157 * 1158 * @exception SWTException <ul> 1159 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1160 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1161 * </ul> 1162 * 1163 * @see List#setSelection(int[]) 1164 */ 1165 public void select (int [] indices) { 1166 checkWidget (); 1167 // SWT extension: allow null for zero length string 1168 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 1169 ptrdiff_t length = indices.length; 1170 if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return; 1171 GtkTreeIter iter; 1172 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 1173 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 1174 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1175 for (int i=0; i<length; i++) { 1176 int index = indices [i]; 1177 if (!(0 <= index && index < count)) continue; 1178 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 1179 OS.gtk_tree_selection_select_iter (selection, &iter); 1180 if ((style & SWT.SINGLE) !is 0) { 1181 auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter); 1182 OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false); 1183 OS.gtk_tree_path_free (path); 1184 } 1185 } 1186 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1187 } 1188 1189 /** 1190 * Selects all of the items in the receiver. 1191 * <p> 1192 * If the receiver is single-select, do nothing. 1193 * 1194 * @exception SWTException <ul> 1195 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1196 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1197 * </ul> 1198 */ 1199 public void selectAll () { 1200 checkWidget(); 1201 if ((style & SWT.SINGLE) !is 0) return; 1202 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 1203 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1204 OS.gtk_tree_selection_select_all (selection); 1205 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1206 } 1207 1208 void selectFocusIndex (int index) { 1209 /* 1210 * Note that this method both selects and sets the focus to the 1211 * specified index, so any previous selection in the list will be lost. 1212 * gtk does not provide a way to just set focus to a specified list item. 1213 */ 1214 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 1215 if (!(0 <= index && index < count)) return; 1216 GtkTreeIter iter; 1217 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 1218 auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter); 1219 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 1220 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1221 OS.gtk_tree_view_set_cursor (cast(GtkTreeView*)handle, path, null, false); 1222 /* 1223 * Bug in GTK. For some reason, when an event loop is run from 1224 * within a key pressed handler and a dialog is displayed that 1225 * contains a GtkTreeView, gtk_tree_view_set_cursor() does 1226 * not set the cursor or select the item. The fix is to select the 1227 * item with gtk_tree_selection_select_iter() as well. 1228 * 1229 * NOTE: This happens in GTK 2.2.1 and is fixed in GTK 2.2.4. 1230 */ 1231 OS.gtk_tree_selection_select_iter (selection, &iter); 1232 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1233 OS.gtk_tree_path_free (path); 1234 } 1235 1236 override void setBackgroundColor (GdkColor* color) { 1237 super.setBackgroundColor (color); 1238 OS.gtk_widget_modify_base (handle, 0, color); 1239 } 1240 1241 override int setBounds (int x, int y, int width, int height, bool move, bool resize) { 1242 int result = super.setBounds (x, y, width, height, move, resize); 1243 /* 1244 * Bug on GTK. The tree view sometimes does not get a paint 1245 * event or resizes to a one pixel square when resized in a new 1246 * shell that is not visible after any event loop has been run. The 1247 * problem is intermittent. It doesn't seem to happen the first time 1248 * a new shell is created. The fix is to ensure the tree view is realized 1249 * after it has been resized. 1250 */ 1251 OS.gtk_widget_realize (handle); 1252 /* 1253 * Bug in GTK. An empty GtkTreeView fails to repaint the focus rectangle 1254 * correctly when resized on versions before 2.6.0. The fix is to force 1255 * the widget to redraw. 1256 */ 1257 if (OS.GTK_VERSION < OS.buildVERSION (2, 6, 0) && OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null) is 0) { 1258 redraw (false); 1259 } 1260 return result; 1261 } 1262 1263 /** 1264 * Sets the text of the item in the receiver's list at the given 1265 * zero-relative index to the string argument. 1266 * 1267 * @param index the index for the item 1268 * @param string the new text for the item 1269 * 1270 * @exception IllegalArgumentException <ul> 1271 * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</li> 1272 * </ul> 1273 * @exception SWTException <ul> 1274 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1275 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1276 * </ul> 1277 */ 1278 public void setItem (int index, String string) { 1279 checkWidget(); 1280 // SWT extension: allow null for zero length string 1281 //if (string is null) error (SWT.ERROR_NULL_ARGUMENT); 1282 if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null))) { 1283 error (SWT.ERROR_INVALID_RANGE); 1284 } 1285 GtkTreeIter iter; 1286 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 1287 char* buffer = string.toStringzValidPtr(); 1288 OS.gtk_list_store_set1 (cast(GtkListStore*)modelHandle, &iter, TEXT_COLUMN, buffer); 1289 } 1290 1291 /** 1292 * Sets the receiver's items to be the given array of items. 1293 * 1294 * @param items the array of items 1295 * 1296 * @exception IllegalArgumentException <ul> 1297 * <li>ERROR_INVALID_ARGUMENT - if an item in the items array is null</li> 1298 * </ul> 1299 * @exception SWTException <ul> 1300 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1301 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1302 * </ul> 1303 */ 1304 public void setItems (String [] items) { 1305 checkWidget(); 1306 // SWT extension, allow null a null length list. 1307 //if (items is null) error (SWT.ERROR_NULL_ARGUMENT); 1308 for (int i=0; i<items.length; i++) { 1309 if (items [i] is null) error (SWT.ERROR_INVALID_ARGUMENT); 1310 } 1311 auto selection = OS.gtk_tree_view_get_selection (cast(GtkTreeView*)handle); 1312 OS.g_signal_handlers_block_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1313 OS.gtk_list_store_clear (cast(GtkListStore*)modelHandle); 1314 OS.g_signal_handlers_unblock_matched (selection, OS.G_SIGNAL_MATCH_DATA, 0, 0, null, null, udCHANGED); 1315 GtkTreeIter iter; 1316 for (int i=0; i<items.length; i++) { 1317 String string = items [i]; 1318 char* buffer = toStringz(string); 1319 OS.gtk_list_store_append (cast(GtkListStore*)modelHandle, &iter); 1320 OS.gtk_list_store_set1 (cast(GtkListStore*)modelHandle, &iter, TEXT_COLUMN, buffer); 1321 } 1322 } 1323 1324 /** 1325 * Selects the item at the given zero-relative index in the receiver. 1326 * If the item at the index was already selected, it remains selected. 1327 * The current selection is first cleared, then the new item is selected. 1328 * Indices that are out of range are ignored. 1329 * 1330 * @param index the index of the item to select 1331 * 1332 * @exception SWTException <ul> 1333 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1334 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1335 * </ul> 1336 * @see List#deselectAll() 1337 * @see List#select(int) 1338 */ 1339 public void setSelection (int index) { 1340 checkWidget (); 1341 deselectAll (); 1342 selectFocusIndex (index); 1343 showSelection (); 1344 } 1345 1346 /** 1347 * Selects the items in the range specified by the given zero-relative 1348 * indices in the receiver. The range of indices is inclusive. 1349 * The current selection is cleared before the new items are selected. 1350 * <p> 1351 * Indices that are out of range are ignored and no items will be selected 1352 * if start is greater than end. 1353 * If the receiver is single-select and there is more than one item in the 1354 * given range, then all indices are ignored. 1355 * 1356 * @param start the start index of the items to select 1357 * @param end the end index of the items to select 1358 * 1359 * @exception SWTException <ul> 1360 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1361 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1362 * </ul> 1363 * 1364 * @see List#deselectAll() 1365 * @see List#select(int,int) 1366 */ 1367 public void setSelection (int start, int end) { 1368 checkWidget (); 1369 deselectAll (); 1370 if (end < 0 || start > end || ((style & SWT.SINGLE) !is 0 && start !is end)) return; 1371 int count = OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null); 1372 if (count is 0 || start >= count) return; 1373 start = Math.max (0, start); 1374 end = Math.min (end, count - 1); 1375 selectFocusIndex (start); 1376 if ((style & SWT.MULTI) !is 0) { 1377 select (start, end); 1378 } 1379 showSelection (); 1380 } 1381 1382 /** 1383 * Selects the items at the given zero-relative indices in the receiver. 1384 * The current selection is cleared before the new items are selected. 1385 * <p> 1386 * Indices that are out of range and duplicate indices are ignored. 1387 * If the receiver is single-select and multiple indices are specified, 1388 * then all indices are ignored. 1389 * 1390 * @param indices the indices of the items to select 1391 * 1392 * @exception SWTException <ul> 1393 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1394 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1395 * </ul> 1396 * 1397 * @see List#deselectAll() 1398 * @see List#select(int[]) 1399 */ 1400 public void setSelection(int[] indices) { 1401 checkWidget (); 1402 // SWT extension: allow null for zero length string 1403 //if (indices is null) error (SWT.ERROR_NULL_ARGUMENT); 1404 deselectAll (); 1405 ptrdiff_t length = indices.length; 1406 if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return; 1407 selectFocusIndex (indices [0]); 1408 if ((style & SWT.MULTI) !is 0) { 1409 select (indices); 1410 } 1411 showSelection (); 1412 } 1413 1414 /** 1415 * Sets the receiver's selection to be the given array of items. 1416 * The current selection is cleared before the new items are selected. 1417 * <p> 1418 * Items that are not in the receiver are ignored. 1419 * If the receiver is single-select and multiple items are specified, 1420 * then all items are ignored. 1421 * 1422 * @param items the array of items 1423 * 1424 * @exception SWTException <ul> 1425 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1426 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1427 * </ul> 1428 * 1429 * @see List#deselectAll() 1430 * @see List#select(int[]) 1431 * @see List#setSelection(int[]) 1432 */ 1433 public void setSelection (String [] items) { 1434 checkWidget (); 1435 // SWT extension: allow null for zero length string 1436 //if (items is null) error (SWT.ERROR_NULL_ARGUMENT); 1437 deselectAll (); 1438 ptrdiff_t length = items.length; 1439 if (length is 0 || ((style & SWT.SINGLE) !is 0 && length > 1)) return; 1440 bool first = true; 1441 for (int i = 0; i < length; i++) { 1442 int index = 0; 1443 String string = items [i]; 1444 if (string !is null) { 1445 while ((index = indexOf (string, index)) !is -1) { 1446 if ((style & SWT.MULTI) !is 0) { 1447 if (first) { 1448 first = false; 1449 selectFocusIndex (index); 1450 } else { 1451 select (index); 1452 } 1453 } else { 1454 selectFocusIndex (index); 1455 break; 1456 } 1457 index++; 1458 } 1459 } 1460 } 1461 showSelection (); 1462 } 1463 1464 /** 1465 * Sets the zero-relative index of the item which is currently 1466 * at the top of the receiver. This index can change when items 1467 * are scrolled or new items are added and removed. 1468 * 1469 * @param index the index of the top item 1470 * 1471 * @exception SWTException <ul> 1472 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1473 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1474 * </ul> 1475 */ 1476 public void setTopIndex (int index) { 1477 checkWidget(); 1478 if (!(0 <= index && index < OS.gtk_tree_model_iter_n_children (cast(GtkTreeStore*)modelHandle, null))) return; 1479 GtkTreeIter iter; 1480 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 1481 auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter); 1482 OS.gtk_tree_view_scroll_to_cell (cast(GtkTreeView*)handle, path, null, true, 0, 0); 1483 if (OS.GTK_VERSION < OS.buildVERSION (2, 8, 0)) { 1484 /* 1485 * Bug in GTK. According to the documentation, gtk_tree_view_scroll_to_cell 1486 * should vertically scroll the cell to the top if use_align is true and row_align is 0. 1487 * However, prior to version 2.8 it does not scroll at all. The fix is to determine 1488 * the new location and use gtk_tree_view_scroll_to_point. 1489 * If the widget is a pinhead, calling gtk_tree_view_scroll_to_point 1490 * will have no effect. Therefore, it is still neccessary to call 1491 * gtk_tree_view_scroll_to_cell. 1492 */ 1493 OS.gtk_widget_realize (handle); 1494 GdkRectangle cellRect; 1495 OS.gtk_tree_view_get_cell_area (cast(GtkTreeView*)handle, path, null, &cellRect); 1496 int tx, ty; 1497 OS.gtk_tree_view_widget_to_tree_coords(cast(GtkTreeView*)handle, cellRect.x, cellRect.y, &tx, &ty); 1498 OS.gtk_tree_view_scroll_to_point (cast(GtkTreeView*)handle, -1, ty); 1499 } 1500 OS.gtk_tree_path_free (path); 1501 } 1502 1503 /** 1504 * Shows the selection. If the selection is already showing in the receiver, 1505 * this method simply returns. Otherwise, the items are scrolled until 1506 * the selection is visible. 1507 * 1508 * @exception SWTException <ul> 1509 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> 1510 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> 1511 * </ul> 1512 */ 1513 public void showSelection () { 1514 checkWidget(); 1515 int index = getSelectionIndex (); 1516 if (index is -1) return; 1517 GtkTreeIter iter; 1518 OS.gtk_tree_model_iter_nth_child (cast(GtkTreeStore*)modelHandle, &iter, null, index); 1519 auto path = OS.gtk_tree_model_get_path (cast(GtkTreeStore*)modelHandle, &iter); 1520 /* 1521 * This code intentionally commented. 1522 * Bug in GTK. According to the documentation, gtk_tree_view_scroll_to_cell 1523 * should scroll the minimum amount to show the cell if use_align is false. 1524 * However, what actually happens is the cell is scrolled to the top. 1525 * The fix is to determine the new location and use gtk_tree_view_scroll_to_point. 1526 * If the widget is a pinhead, calling gtk_tree_view_scroll_to_point 1527 * will have no effect. Therefore, it is still neccessary to 1528 * call gtk_tree_view_scroll_to_cell. 1529 */ 1530 // OS.gtk_tree_view_scroll_to_cell (handle, path, 0, false, 0, 0); 1531 OS.gtk_widget_realize (handle); 1532 GdkRectangle visibleRect; 1533 OS.gtk_tree_view_get_visible_rect (cast(GtkTreeView*)handle, &visibleRect); 1534 GdkRectangle cellRect; 1535 OS.gtk_tree_view_get_cell_area (cast(GtkTreeView*)handle, path, null, &cellRect); 1536 int tx, ty; 1537 OS.gtk_tree_view_widget_to_tree_coords(cast(GtkTreeView*)handle, cellRect.x, cellRect.y, &tx, &ty); 1538 if (ty < visibleRect.y ) { 1539 OS.gtk_tree_view_scroll_to_cell (cast(GtkTreeView*)handle, path, null, true, 0f, 0f); 1540 OS.gtk_tree_view_scroll_to_point (cast(GtkTreeView*)handle, -1, ty); 1541 } else { 1542 int height = Math.min (visibleRect.height, cellRect.height); 1543 if (ty + height > visibleRect.y + visibleRect.height) { 1544 OS.gtk_tree_view_scroll_to_cell (cast(GtkTreeView*)handle, path, null, true, 1f, 0f); 1545 ty += cellRect.height - visibleRect.height; 1546 OS.gtk_tree_view_scroll_to_point (cast(GtkTreeView*)handle, -1, ty); 1547 } 1548 } 1549 OS.gtk_tree_path_free (path); 1550 } 1551 1552 override void treeSelectionProc (GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter, int[] selection, int length_) { 1553 if (selection !is null) { 1554 auto indices = OS.gtk_tree_path_get_indices (path); 1555 if (indices !is null) { 1556 selection [length_] = indices[0]; 1557 } 1558 } 1559 } 1560 1561 }