1 /*******************************************************************************
2 * Copyright (c) 2011, 2016 IBM Corporation and others.
4 * This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License 2.0
6 * which accompanies this distribution, and is available at
7 * https://www.eclipse.org/legal/epl-2.0/
9 * SPDX-License-Identifier: EPL-2.0
12 * IBM Corporation - initial API and implementation
13 *******************************************************************************/
14 package org.eclipse.swt.custom;
17 import org.eclipse.swt.*;
18 import org.eclipse.swt.accessibility.*;
19 import org.eclipse.swt.events.*;
20 import org.eclipse.swt.graphics.*;
21 import org.eclipse.swt.widgets.*;
24 * A TreeCursor provides a way for the user to navigate around a Tree with columns using the
25 * keyboard. It also provides a mechanism for selecting an individual cell in a tree.
27 * For a detailed example of using a TreeCursor to navigate to a cell and then edit it see
28 * http://git.eclipse.org/c/platform/eclipse.platform.swt.git/tree/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet360.java .
31 * <dt><b>Styles:</b></dt>
33 * <dt><b>Events:</b></dt>
34 * <dd>Selection, DefaultSelection</dd>
39 public class TreeCursor extends Canvas {
43 Listener listener, treeListener, resizeListener, disposeItemListener, disposeColumnListener;
45 Color background = null;
46 Color foreground = null;
48 /* By default, invert the list selection colors */
49 static final int BACKGROUND = SWT.COLOR_LIST_SELECTION_TEXT;
50 static final int FOREGROUND = SWT.COLOR_LIST_SELECTION;
53 * Constructs a new instance of this class given its parent tree and a style value describing
54 * its behavior and appearance.
56 * The style value is either one of the style constants defined in class <code>SWT</code> which
57 * is applicable to instances of this class, or must be built by <em>bitwise OR</em>'ing
58 * together (that is, using the <code>int</code> "|" operator) two or more of those
59 * <code>SWT</code> style constants. The class description lists the style constants that are
60 * applicable to the class. Style bits are also inherited from superclasses.
63 * @param parent a Tree control which will be the parent of the new instance (cannot be null)
64 * @param style the style of control to construct
66 * @exception IllegalArgumentException <ul>
67 * <li>ERROR_NULL_ARGUMENT - if the parent is null</li>
69 * @exception SWTException <ul>
70 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li>
71 * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li>
75 * @see Widget#checkSubclass()
76 * @see Widget#getStyle()
78 public TreeCursor(Tree parent, int style) {
87 * Detect cases where the cursor position has become invalid and fix it.
88 * The typical cause of this is programmatic tree changes, such as
89 * expanding/collapsing and item and creating/disposing items.
91 if (row.isDisposed()) {
92 unhookRowColumnListeners();
97 TreeItem current = row;
98 TreeItem parentItem = row.getParentItem();
99 while (parentItem != null && !parentItem.getExpanded()) {
100 current = parentItem;
101 parentItem = current.getParentItem();
103 if (current != row) {
104 setRowColumn(current, column, false);
107 switch (event.type) {
123 switch (event.detail) {
124 case SWT.TRAVERSE_ARROW_NEXT:
125 case SWT.TRAVERSE_ARROW_PREVIOUS:
126 case SWT.TRAVERSE_RETURN:
133 int[] events = new int[] { SWT.Dispose, SWT.FocusIn, SWT.FocusOut, SWT.KeyDown, SWT.Paint, SWT.Traverse };
134 for (int i = 0; i < events.length; i++) {
135 addListener(events[i], listener);
138 treeListener = event -> {
139 switch (event.type) {
150 treeMouseDown(event);
154 tree.addListener(SWT.Collapse, treeListener);
155 tree.addListener(SWT.Expand, treeListener);
156 tree.addListener(SWT.FocusIn, treeListener);
157 tree.addListener(SWT.MouseDown, treeListener);
159 disposeItemListener = event -> {
160 TreeItem currentItem = row;
161 while (currentItem != null) {
162 currentItem.removeListener(SWT.Dispose, disposeItemListener);
163 currentItem = currentItem.getParentItem();
165 TreeItem disposedItem = (TreeItem)event.widget;
166 TreeItem parentItem = disposedItem.getParentItem();
167 if (parentItem != null) {
168 setRowColumn(parentItem, column, true);
170 if (tree.getItemCount() == 1) {
171 unhookRowColumnListeners();
173 TreeItem newFocus = null;
174 int rowIndex = tree.indexOf(disposedItem);
176 TreeItem previousItem = tree.getItem(rowIndex - 1);
177 if (!previousItem.isDisposed()) {
178 newFocus = previousItem;
181 if (newFocus == null && rowIndex + 1 < tree.getItemCount()) {
182 TreeItem nextItem = tree.getItem(rowIndex + 1);
183 if (!nextItem.isDisposed()) {
187 if (newFocus != null) {
188 setRowColumn(newFocus, column, true);
190 unhookRowColumnListeners();
196 disposeColumnListener = event -> {
197 if (column != null) {
198 if (tree.getColumnCount() == 1) {
201 int columnIndex = tree.indexOf(column);
202 int positionIndex = columnIndex;
203 int[] columnOrder = tree.getColumnOrder();
204 for (int i = 0; i < columnOrder.length; i++) {
205 if (columnOrder[i] == columnIndex) {
210 if (positionIndex == columnOrder.length - 1) {
211 setRowColumn(row, tree.getColumn(columnOrder[positionIndex - 1]), true);
213 setRowColumn(row, tree.getColumn(columnOrder[positionIndex + 1]), true);
219 resizeListener = event -> _resize();
220 ScrollBar hBar = tree.getHorizontalBar();
222 hBar.addListener(SWT.Selection, resizeListener);
224 ScrollBar vBar = tree.getVerticalBar();
226 vBar.addListener(SWT.Selection, resizeListener);
229 getAccessible().addAccessibleControlListener(new AccessibleControlAdapter() {
231 public void getRole(AccessibleControlEvent e) {
232 e.detail = ACC.ROLE_TABLECELL;
235 getAccessible().addAccessibleListener(new AccessibleAdapter() {
237 public void getName(AccessibleEvent e) {
238 if (row == null) return;
239 int columnIndex = column == null ? 0 : tree.indexOf(column);
240 e.result = row.getText(columnIndex);
246 * Adds the listener to the collection of listeners who will be notified when the receiver's
247 * selection changes, by sending it one of the messages defined in the
248 * <code>SelectionListener</code> interface.
250 * When <code>widgetSelected</code> is called, the item field of the event object is valid. If
251 * the receiver has <code>SWT.CHECK</code> style set and the check selection changes, the event
252 * object detail field contains the value <code>SWT.CHECK</code>.
253 * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked.
256 * @param listener the listener which should be notified
258 * @exception IllegalArgumentException <ul>
259 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
261 * @exception SWTException <ul>
262 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
263 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
266 * @see SelectionListener
267 * @see SelectionEvent
268 * @see #removeSelectionListener(SelectionListener)
270 public void addSelectionListener(SelectionListener listener) {
272 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
273 TypedListener typedListener = new TypedListener(listener);
274 addListener(SWT.Selection, typedListener);
275 addListener(SWT.DefaultSelection, typedListener);
278 int countSubTreePages(TreeItem root) {
280 if (root == null) return 0;
281 if (root.getItemCount() == 0) return 1;
282 if (!root.getExpanded()) return 1;
283 TreeItem[] items = root.getItems();
284 for (int i = 0; i < items.length; i++) {
285 pages += countSubTreePages(items[i]);
290 int findIndex(TreeItem[] items, TreeItem treeItem) {
291 if (items == null || treeItem == null) return -1;
292 Rectangle rect = treeItem.getBounds();
294 for (int i = 0; i < items.length; i++) {
295 TreeItem previousItem = null;
296 TreeItem currentItem = items[i];
297 if (i > 0) previousItem = items[i - 1];
298 Rectangle rect1 = currentItem.getBounds();
299 if (rect.y == rect1.y) return index;
300 if (rect.y < rect1.y) {
301 return index - 1 + findIndex(previousItem.getItems(), treeItem);
303 if (rect.y > rect1.y && i == items.length - 1) {
304 return index + findIndex(currentItem.getItems(), treeItem);
306 if (rect.y >= rect1.y + (1 + currentItem.getItemCount()) * tree.getItemHeight() && currentItem.getExpanded()) {
307 index += countSubTreePages(currentItem);
315 TreeItem findItem(TreeItem[] items, Point pt) {
316 int start = 0, end = items.length - 1;
318 while (end - start > 1) {
319 TreeItem currentItem = items[index];
320 Rectangle bounds = currentItem.getBounds();
321 if (pt.y < bounds.y) {
323 index = (end - start) / 2;
326 index = start + ((end - start) / 2);
330 Rectangle endBounds = items[end].getBounds();
331 if (endBounds.y < pt.y) {
332 if (endBounds.y + endBounds.height < pt.y) {
333 if (!items[end].getExpanded()) return null;
334 return findItem(items[end].getItems(), pt);
336 int[] columnOrder = tree.getColumnOrder();
337 Rectangle bounds = null;
338 if (columnOrder.length > 0) {
339 Rectangle rect1 = items[end].getBounds(columnOrder[0]);
340 Rectangle rect2 = items[end].getBounds(columnOrder[columnOrder.length - 1]);
341 bounds = rect1.union(rect2);
342 bounds.height += tree.getLinesVisible() ? tree.getGridLineWidth() : 0;
344 bounds = items[end].getBounds();
346 return bounds.contains(pt) ? items[end] : null;
349 Rectangle startBounds = items[start].getBounds();
350 if (startBounds.y + startBounds.height < pt.y) {
351 return findItem(items[start].getItems(), pt);
353 int[] columnOrder = tree.getColumnOrder();
354 Rectangle bounds = null;
355 if (columnOrder.length > 0) {
356 Rectangle rect1 = items[start].getBounds(columnOrder[0]);
357 Rectangle rect2 = items[start].getBounds(columnOrder[columnOrder.length - 1]);
358 bounds = rect1.union(rect2);
359 bounds.height += tree.getLinesVisible() ? tree.getGridLineWidth() : 0;
361 bounds = items[start].getBounds();
363 return bounds.contains(pt) ? items[start] : null;
367 * Returns the background color that the receiver will use to draw.
369 * @return the receiver's background color
372 public Color getBackground() {
374 if (background == null) {
375 return getDisplay().getSystemColor(BACKGROUND);
381 * Returns the index of the column over which the TreeCursor is positioned.
383 * @return the column index for the current position
385 * @exception SWTException <ul>
386 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
387 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
390 public int getColumn() {
392 return column == null ? 0 : tree.indexOf(column);
396 * Returns the foreground color that the receiver will use to draw.
398 * @return the receiver's foreground color
401 public Color getForeground() {
403 if (foreground == null) {
404 return getDisplay().getSystemColor(FOREGROUND);
409 TreeItem getLastVisibleItem(TreeItem[] items) {
410 if (items == null) return null;
411 TreeItem last = items[items.length - 1];
412 if (last.getExpanded() && last.getItemCount() > 0) {
413 return getLastVisibleItem(last.getItems());
418 TreeItem getNextItem(TreeItem item) {
419 if (item == null) return null;
420 if (item.getExpanded() && item.getItemCount() > 0) {
421 return item.getItem(0);
424 TreeItem parentItem = item.getParentItem();
425 while (parentItem != null) {
426 int index = parentItem.indexOf(item);
427 if (index == -1) return null;
428 if (index < parentItem.getItemCount() - 1) {
429 return parentItem.getItem(index + 1);
432 parentItem = item.getParentItem();
434 int index = tree.indexOf(item);
435 if (index == -1) return null;
436 if (index == tree.getItemCount() - 1) return null;
437 return tree.getItem(index + 1);
440 TreeItem getPreviousItem(TreeItem item) {
441 if (item == null) return null;
442 TreeItem parentItem = item.getParentItem();
443 if (parentItem == null) {
444 int index = tree.indexOf(item);
445 if (index == -1 || index == 0) return null;
446 item = tree.getItem(index - 1);
447 if (item.getExpanded() && item.getItemCount() > 0) {
448 return getLastVisibleItem(item.getItems());
452 int index = parentItem.indexOf(item);
453 if (index == -1) return null;
454 if (index == 0) return parentItem;
455 item = parentItem.getItem(index - 1);
456 if (item.getExpanded() && item.getItemCount() > 0) {
457 return getLastVisibleItem(item.getItems());
463 * Returns the row over which the TreeCursor is positioned.
465 * @return the item for the current position, or <code>null</code> if none
467 * @exception SWTException <ul>
468 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
469 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
472 public TreeItem getRow() {
477 void keyDown(Event event) {
478 if (row == null) return;
479 switch (event.character) {
481 notifyListeners(SWT.DefaultSelection, new Event());
484 switch (event.keyCode) {
486 TreeItem previousItem = getPreviousItem(row);
487 if (previousItem != null) {
488 setRowColumn(previousItem, column, true);
492 TreeItem nextItem = getNextItem(row);
493 if (nextItem != null) {
494 setRowColumn(nextItem, column, true);
498 case SWT.ARROW_RIGHT: {
499 if ((event.stateMask & SWT.MOD1) != 0) {
500 row.setExpanded (event.keyCode == SWT.ARROW_RIGHT);
503 int columnCount = tree.getColumnCount();
504 if (columnCount == 0) break;
505 int columnIndex = column == null ? 0 : tree.indexOf(column);
506 int[] columnOrder = tree.getColumnOrder();
508 while (index < columnOrder.length) {
509 if (columnOrder[index] == columnIndex) break;
512 if (index == columnOrder.length) index = 0;
513 int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT;
514 TreeItem parentRow = row.getParentItem();
515 int rowIndex = tree.indexOf(row);
516 if (event.keyCode == leadKey) {
517 if (parentRow != null) {
518 setRowColumn(row, tree.getColumn(columnOrder[Math.max(0, index - 1)]), true);
520 setRowColumn(rowIndex, columnOrder[Math.max(0, index - 1)], true);
523 if (parentRow != null) {
524 setRowColumn(row, tree.getColumn(columnOrder[Math.min(columnCount - 1, index + 1)]), true);
526 setRowColumn(rowIndex, columnOrder[Math.min(columnCount - 1, index + 1)], true);
532 int columnIndex = column == null ? 0 : tree.indexOf(column);
533 setRowColumn(0, columnIndex, true);
536 TreeItem[] items = tree.getItems();
537 setRowColumn(getLastVisibleItem(items), column, true);
541 Rectangle rect = tree.getClientArea();
542 Rectangle itemRect = tree.getTopItem().getBounds();
544 int index = findIndex(tree.getItems(), item);
545 int itemHeight = tree.getItemHeight();
546 rect.height -= itemRect.y;
547 int page = Math.max(1, rect.height / itemHeight);
548 if (index - page <= 0) {
549 TreeItem first = tree.getItem(0);
550 setRowColumn(first, column, true);
553 for (int i = 0; i < page; i++) {
554 item = getPreviousItem(item);
556 setRowColumn(item, column, true);
559 case SWT.PAGE_DOWN: {
560 Rectangle rect = tree.getClientArea();
561 Rectangle itemRect = tree.getTopItem().getBounds();
563 int index = findIndex(tree.getItems(), item);
564 int height = tree.getItemHeight();
565 rect.height -= itemRect.y;
566 TreeItem last = getLastVisibleItem(tree.getItems());
567 int page = Math.max(1, rect.height / height);
568 int end = findIndex(tree.getItems(), last);
569 if (end <= index + page) {
570 setRowColumn(last, column, true);
573 for (int i = 0; i < page; i++) {
574 item = getNextItem(item);
576 setRowColumn(item, column, true);
582 void onDispose(Event event) {
583 removeListener(SWT.Dispose, listener);
584 notifyListeners(SWT.Dispose, event);
585 event.type = SWT.None;
587 tree.removeListener(SWT.Collapse, treeListener);
588 tree.removeListener(SWT.Expand, treeListener);
589 tree.removeListener(SWT.FocusIn, treeListener);
590 tree.removeListener(SWT.MouseDown, treeListener);
591 unhookRowColumnListeners();
592 ScrollBar hBar = tree.getHorizontalBar();
594 hBar.removeListener(SWT.Selection, resizeListener);
596 ScrollBar vBar = tree.getVerticalBar();
598 vBar.removeListener(SWT.Selection, resizeListener);
602 void paint(Event event) {
603 if (row == null) return;
604 int columnIndex = column == null ? 0 : tree.indexOf(column);
605 int orderedIndex = columnIndex;
606 int[] columnOrder = tree.getColumnOrder();
607 for (int i = 0; i < columnOrder.length; i++) {
608 if (columnOrder[i] == columnIndex) {
614 gc.setBackground(getBackground());
615 gc.setForeground(getForeground());
616 gc.fillRectangle(event.x, event.y, event.width, event.height);
617 Image image = row.getImage(columnIndex);
619 // Temporary code - need a better way to determine trim
620 String platform = SWT.getPlatform();
622 if ("win32".equals(platform)) { //$NON-NLS-1$
623 if (orderedIndex > 0) {
630 Point size = getSize();
632 Rectangle imageSize = image.getBounds();
633 int imageY = (size.y - imageSize.height) / 2;
634 gc.drawImage(image, x, imageY);
635 x += imageSize.width;
637 String text = row.getText(columnIndex);
638 if (text.length() > 0) {
639 Rectangle bounds = row.getBounds(columnIndex);
640 Point extent = gc.stringExtent(text);
641 // Temporary code - need a better way to determine trim
642 if ("win32".equals(platform)) { //$NON-NLS-1$
643 if (tree.getColumnCount() == 0 || orderedIndex == 0) {
644 x += image == null ? 2 : 5;
646 int alignmnent = column.getAlignment();
647 switch (alignmnent) {
649 x += image == null ? 5 : 3;
652 x = bounds.width - extent.x - 2;
655 x += Math.ceil((bounds.width - x - extent.x) / 2.0);
660 if (tree.getColumnCount() == 0) {
661 x += image == null ? 4 : 3;
663 int alignmnent = column.getAlignment();
664 switch (alignmnent) {
666 x += image == null ? 5 : 3;
669 x = bounds.width - extent.x - 2;
672 x += (bounds.width - x - extent.x) / 2 + 2;
677 int textY = (size.y - extent.y) / 2;
678 gc.drawString(text, x, textY);
680 if (isFocusControl()) {
681 Display display = getDisplay();
682 gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK));
683 gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE));
684 gc.drawFocus(0, 0, size.x, size.y);
689 * Removes the listener from the collection of listeners who will be notified when the
690 * receiver's selection changes.
692 * @param listener the listener which should no longer be notified
694 * @exception IllegalArgumentException <ul>
695 * <li>ERROR_NULL_ARGUMENT - if the listener is null</li>
697 * @exception SWTException <ul>
698 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
699 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
702 * @see SelectionListener
703 * @see #addSelectionListener(SelectionListener)
705 public void removeSelectionListener(SelectionListener listener) {
707 if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
708 removeListener(SWT.Selection, listener);
709 removeListener(SWT.DefaultSelection, listener);
714 setBounds(-200, -200, 0, 0);
716 int columnIndex = column == null ? 0 : tree.indexOf(column);
717 setBounds(row.getBounds(columnIndex));
722 * Sets the receiver's background color to the color specified
723 * by the argument, or to the default system color for the control
724 * if the argument is null.
726 * Note: This operation is a hint and may be overridden by the platform.
727 * For example, on Windows the background of a Button cannot be changed.
729 * @param color the new color (or null)
731 * @exception IllegalArgumentException <ul>
732 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
734 * @exception SWTException <ul>
735 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
736 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
740 public void setBackground (Color color) {
742 super.setBackground(getBackground());
746 * Sets the receiver's foreground color to the color specified
747 * by the argument, or to the default system color for the control
748 * if the argument is null.
750 * Note: This operation is a hint and may be overridden by the platform.
752 * @param color the new color (or null)
754 * @exception IllegalArgumentException <ul>
755 * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li>
757 * @exception SWTException <ul>
758 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
759 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
763 public void setForeground (Color color) {
765 super.setForeground(getForeground());
769 void setRowColumn(int row, int column, boolean notify) {
770 TreeItem item = row == -1 ? null : tree.getItem(row);
771 TreeColumn col = column == -1 || tree.getColumnCount() == 0 ? null : tree.getColumn(column);
772 setRowColumn(item, col, notify);
775 void setRowColumn(TreeItem row, TreeColumn column, boolean notify) {
776 if (this.row != null && this.row != row) {
777 TreeItem currentItem = this.row;
778 while (currentItem != null) {
779 currentItem.removeListener(SWT.Dispose, disposeItemListener);
780 currentItem = currentItem.getParentItem();
784 if (this.column != null && this.column != column) {
785 this.column.removeListener(SWT.Dispose, disposeColumnListener);
786 this.column.removeListener(SWT.Move, resizeListener);
787 this.column.removeListener(SWT.Resize, resizeListener);
791 if (this.row != row) {
793 TreeItem currentItem = row;
794 while (currentItem != null) {
795 currentItem.addListener(SWT.Dispose, disposeItemListener);
796 currentItem = currentItem.getParentItem();
800 if (this.column != column && column != null) {
801 this.column = column;
802 column.addListener(SWT.Dispose, disposeColumnListener);
803 column.addListener(SWT.Move, resizeListener);
804 column.addListener(SWT.Resize, resizeListener);
805 tree.showColumn(column);
807 int columnIndex = column == null ? 0 : tree.indexOf(column);
808 setBounds(row.getBounds(columnIndex));
810 if (notify) notifyListeners(SWT.Selection, new Event());
815 * Positions the TreeCursor over the root-level cell at the given row and column in the parent tree.
817 * @param row the index of the root-level row for the cell to select
818 * @param column the index of column for the cell to select
820 * @exception SWTException <ul>
821 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
822 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
825 public void setSelection(int row, int column) {
827 int columnCount = tree.getColumnCount();
828 int maxColumnIndex = columnCount == 0 ? 0 : columnCount - 1;
829 if (row < 0 || row >= tree.getItemCount() || column < 0 || column > maxColumnIndex) {
830 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
832 setRowColumn(row, column, false);
836 * Positions the TreeCursor over the cell at the given row and column in the parent tree.
838 * @param row the TreeItem of the row for the cell to select
839 * @param column the index of column for the cell to select
841 * @exception SWTException <ul>
842 * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
843 * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
846 public void setSelection(TreeItem row, int column) {
848 int columnCount = tree.getColumnCount();
849 int maxColumnIndex = columnCount == 0 ? 0 : columnCount - 1;
850 if (row == null || row.isDisposed() || column < 0 || column > maxColumnIndex) {
851 SWT.error(SWT.ERROR_INVALID_ARGUMENT);
853 TreeColumn col = tree.getColumnCount() == 0 ? null : tree.getColumn(column);
854 setRowColumn(row, col, false);
858 public void setVisible(boolean visible) {
863 super.setVisible(visible);
866 void treeCollapse(Event event) {
867 if (row == null) return;
868 TreeItem root = (TreeItem)event.item;
869 TreeItem parentItem = row.getParentItem();
870 while (parentItem != null) {
871 if (parentItem == root) {
872 setRowColumn(root, column, true);
875 parentItem = parentItem.getParentItem();
878 getDisplay().asyncExec(() -> {
879 if (isDisposed()) return;
880 setRowColumn(row, column, true);
884 void treeExpand(Event event) {
885 getDisplay().asyncExec(() -> {
886 if (isDisposed()) return;
887 setRowColumn(row, column, true);
891 void treeFocusIn(Event event) {
893 if (row == null && column == null) return;
898 void treeMouseDown(Event event) {
899 if (tree.getItemCount() == 0) return;
900 Point pt = new Point(event.x, event.y);
901 TreeItem item = tree.getItem(pt);
902 if (item == null && (tree.getStyle() & SWT.FULL_SELECTION) == 0) {
903 TreeItem currentItem = tree.getTopItem();
904 TreeItem parentItem = currentItem.getParentItem();
905 while (parentItem != null) {
906 currentItem = parentItem;
907 parentItem = currentItem.getParentItem();
909 int start = tree.indexOf(currentItem);
910 int viewportItemCount = tree.getClientArea().height / tree.getItemHeight();
911 int end = Math.min(start + viewportItemCount, tree.getItemCount() - 1);
912 TreeItem[] allItems = tree.getItems();
913 TreeItem[] items = new TreeItem[end - start + 1];
914 System.arraycopy(allItems, start, items, 0, end - start + 1);
915 item = findItem(items, pt);
917 if (item == null) return;
919 TreeColumn newColumn = null;
920 int lineWidth = tree.getLinesVisible() ? tree.getGridLineWidth() : 0;
921 int columnCount = tree.getColumnCount();
922 if (columnCount > 0) {
923 for (int i = 0; i < columnCount; i++) {
924 Rectangle rect = item.getBounds(i);
925 rect.width += lineWidth;
926 rect.height += lineWidth;
927 if (rect.contains(pt)) {
928 newColumn = tree.getColumn(i);
932 if (newColumn == null) {
933 newColumn = tree.getColumn(0);
936 setRowColumn(item, newColumn, true);
940 void unhookRowColumnListeners() {
941 if (column != null && !column.isDisposed()) {
942 column.removeListener(SWT.Dispose, disposeColumnListener);
943 column.removeListener(SWT.Move, resizeListener);
944 column.removeListener(SWT.Resize, resizeListener);
947 if (row != null && !row.isDisposed()) {
948 TreeItem currentItem = row;
949 while (currentItem != null) {
950 currentItem.removeListener(SWT.Dispose, disposeItemListener);
951 currentItem = currentItem.getParentItem();