| /******************************************************************************* |
| * Copyright (c) 2011, 2016 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.swt.custom; |
| |
| |
| import org.eclipse.swt.*; |
| import org.eclipse.swt.accessibility.*; |
| import org.eclipse.swt.events.*; |
| import org.eclipse.swt.graphics.*; |
| import org.eclipse.swt.widgets.*; |
| |
| /** |
| * A TreeCursor provides a way for the user to navigate around a Tree with columns using the |
| * keyboard. It also provides a mechanism for selecting an individual cell in a tree. |
| * <p> |
| * For a detailed example of using a TreeCursor to navigate to a cell and then edit it see |
| * http://git.eclipse.org/c/platform/eclipse.platform.swt.git/tree/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/Snippet360.java . |
| * |
| * <dl> |
| * <dt><b>Styles:</b></dt> |
| * <dd>BORDER</dd> |
| * <dt><b>Events:</b></dt> |
| * <dd>Selection, DefaultSelection</dd> |
| * </dl> |
| * |
| * @since 3.8 |
| */ |
| public class TreeCursor extends Canvas { |
| Tree tree; |
| TreeItem row; |
| TreeColumn column; |
| Listener listener, treeListener, resizeListener, disposeItemListener, disposeColumnListener; |
| |
| Color background = null; |
| Color foreground = null; |
| |
| /* By default, invert the list selection colors */ |
| static final int BACKGROUND = SWT.COLOR_LIST_SELECTION_TEXT; |
| static final int FOREGROUND = SWT.COLOR_LIST_SELECTION; |
| |
| /** |
| * Constructs a new instance of this class given its parent tree and a style value describing |
| * its behavior and appearance. |
| * <p> |
| * The style value is either one of the style constants defined in class <code>SWT</code> which |
| * is applicable to instances of this class, or must be built by <em>bitwise OR</em>'ing |
| * together (that is, using the <code>int</code> "|" operator) two or more of those |
| * <code>SWT</code> style constants. The class description lists the style constants that are |
| * applicable to the class. Style bits are also inherited from superclasses. |
| * </p> |
| * |
| * @param parent a Tree control which will be the parent of the new instance (cannot be null) |
| * @param style the style of control to construct |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the parent</li> |
| * <li>ERROR_INVALID_SUBCLASS - if this class is not an allowed subclass</li> |
| * </ul> |
| * |
| * @see SWT#BORDER |
| * @see Widget#checkSubclass() |
| * @see Widget#getStyle() |
| */ |
| public TreeCursor(Tree parent, int style) { |
| super(parent, style); |
| tree = parent; |
| setBackground(null); |
| setForeground(null); |
| |
| listener = event -> { |
| if (row != null) { |
| /* |
| * Detect cases where the cursor position has become invalid and fix it. |
| * The typical cause of this is programmatic tree changes, such as |
| * expanding/collapsing and item and creating/disposing items. |
| */ |
| if (row.isDisposed()) { |
| unhookRowColumnListeners(); |
| _resize(); |
| tree.setFocus(); |
| return; |
| } |
| TreeItem current = row; |
| TreeItem parentItem = row.getParentItem(); |
| while (parentItem != null && !parentItem.getExpanded()) { |
| current = parentItem; |
| parentItem = current.getParentItem(); |
| } |
| if (current != row) { |
| setRowColumn(current, column, false); |
| } |
| } |
| switch (event.type) { |
| case SWT.Dispose: |
| onDispose(event); |
| break; |
| case SWT.FocusIn: |
| case SWT.FocusOut: |
| redraw(); |
| break; |
| case SWT.KeyDown: |
| keyDown(event); |
| break; |
| case SWT.Paint: |
| paint(event); |
| break; |
| case SWT.Traverse: |
| event.doit = true; |
| switch (event.detail) { |
| case SWT.TRAVERSE_ARROW_NEXT: |
| case SWT.TRAVERSE_ARROW_PREVIOUS: |
| case SWT.TRAVERSE_RETURN: |
| event.doit = false; |
| break; |
| } |
| break; |
| } |
| }; |
| int[] events = new int[] { SWT.Dispose, SWT.FocusIn, SWT.FocusOut, SWT.KeyDown, SWT.Paint, SWT.Traverse }; |
| for (int i = 0; i < events.length; i++) { |
| addListener(events[i], listener); |
| } |
| |
| treeListener = event -> { |
| switch (event.type) { |
| case SWT.Collapse: |
| treeCollapse(event); |
| break; |
| case SWT.Expand: |
| treeExpand(event); |
| break; |
| case SWT.FocusIn: |
| treeFocusIn(event); |
| break; |
| case SWT.MouseDown: |
| treeMouseDown(event); |
| break; |
| } |
| }; |
| tree.addListener(SWT.Collapse, treeListener); |
| tree.addListener(SWT.Expand, treeListener); |
| tree.addListener(SWT.FocusIn, treeListener); |
| tree.addListener(SWT.MouseDown, treeListener); |
| |
| disposeItemListener = event -> { |
| TreeItem currentItem = row; |
| while (currentItem != null) { |
| currentItem.removeListener(SWT.Dispose, disposeItemListener); |
| currentItem = currentItem.getParentItem(); |
| } |
| TreeItem disposedItem = (TreeItem)event.widget; |
| TreeItem parentItem = disposedItem.getParentItem(); |
| if (parentItem != null) { |
| setRowColumn(parentItem, column, true); |
| } else { |
| if (tree.getItemCount() == 1) { |
| unhookRowColumnListeners(); |
| } else { |
| TreeItem newFocus = null; |
| int rowIndex = tree.indexOf(disposedItem); |
| if (rowIndex != 0) { |
| TreeItem previousItem = tree.getItem(rowIndex - 1); |
| if (!previousItem.isDisposed()) { |
| newFocus = previousItem; |
| } |
| } |
| if (newFocus == null && rowIndex + 1 < tree.getItemCount()) { |
| TreeItem nextItem = tree.getItem(rowIndex + 1); |
| if (!nextItem.isDisposed()) { |
| newFocus = nextItem; |
| } |
| } |
| if (newFocus != null) { |
| setRowColumn(newFocus, column, true); |
| } else { |
| unhookRowColumnListeners(); |
| } |
| } |
| } |
| _resize(); |
| }; |
| disposeColumnListener = event -> { |
| if (column != null) { |
| if (tree.getColumnCount() == 1) { |
| column = null; |
| } else { |
| int columnIndex = tree.indexOf(column); |
| int positionIndex = columnIndex; |
| int[] columnOrder = tree.getColumnOrder(); |
| for (int i = 0; i < columnOrder.length; i++) { |
| if (columnOrder[i] == columnIndex) { |
| positionIndex = i; |
| break; |
| } |
| } |
| if (positionIndex == columnOrder.length - 1) { |
| setRowColumn(row, tree.getColumn(columnOrder[positionIndex - 1]), true); |
| } else { |
| setRowColumn(row, tree.getColumn(columnOrder[positionIndex + 1]), true); |
| } |
| } |
| } |
| _resize(); |
| }; |
| resizeListener = event -> _resize(); |
| ScrollBar hBar = tree.getHorizontalBar(); |
| if (hBar != null) { |
| hBar.addListener(SWT.Selection, resizeListener); |
| } |
| ScrollBar vBar = tree.getVerticalBar(); |
| if (vBar != null) { |
| vBar.addListener(SWT.Selection, resizeListener); |
| } |
| |
| getAccessible().addAccessibleControlListener(new AccessibleControlAdapter() { |
| @Override |
| public void getRole(AccessibleControlEvent e) { |
| e.detail = ACC.ROLE_TABLECELL; |
| } |
| }); |
| getAccessible().addAccessibleListener(new AccessibleAdapter() { |
| @Override |
| public void getName(AccessibleEvent e) { |
| if (row == null) return; |
| int columnIndex = column == null ? 0 : tree.indexOf(column); |
| e.result = row.getText(columnIndex); |
| } |
| }); |
| } |
| |
| /** |
| * Adds the listener to the collection of listeners who will be notified when the receiver's |
| * selection changes, by sending it one of the messages defined in the |
| * <code>SelectionListener</code> interface. |
| * <p> |
| * When <code>widgetSelected</code> is called, the item field of the event object is valid. If |
| * the receiver has <code>SWT.CHECK</code> style set and the check selection changes, the event |
| * object detail field contains the value <code>SWT.CHECK</code>. |
| * <code>widgetDefaultSelected</code> is typically called when an item is double-clicked. |
| * </p> |
| * |
| * @param listener the listener which should be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SelectionListener |
| * @see SelectionEvent |
| * @see #removeSelectionListener(SelectionListener) |
| */ |
| public void addSelectionListener(SelectionListener listener) { |
| checkWidget(); |
| if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| TypedListener typedListener = new TypedListener(listener); |
| addListener(SWT.Selection, typedListener); |
| addListener(SWT.DefaultSelection, typedListener); |
| } |
| |
| int countSubTreePages(TreeItem root) { |
| int pages = 1; |
| if (root == null) return 0; |
| if (root.getItemCount() == 0) return 1; |
| if (!root.getExpanded()) return 1; |
| TreeItem[] items = root.getItems(); |
| for (int i = 0; i < items.length; i++) { |
| pages += countSubTreePages(items[i]); |
| } |
| return pages; |
| } |
| |
| int findIndex(TreeItem[] items, TreeItem treeItem) { |
| if (items == null || treeItem == null) return -1; |
| Rectangle rect = treeItem.getBounds(); |
| int index = 0; |
| for (int i = 0; i < items.length; i++) { |
| TreeItem previousItem = null; |
| TreeItem currentItem = items[i]; |
| if (i > 0) previousItem = items[i - 1]; |
| Rectangle rect1 = currentItem.getBounds(); |
| if (rect.y == rect1.y) return index; |
| if (rect.y < rect1.y) { |
| return index - 1 + findIndex(previousItem.getItems(), treeItem); |
| } |
| if (rect.y > rect1.y && i == items.length - 1) { |
| return index + findIndex(currentItem.getItems(), treeItem); |
| } |
| if (rect.y >= rect1.y + (1 + currentItem.getItemCount()) * tree.getItemHeight() && currentItem.getExpanded()) { |
| index += countSubTreePages(currentItem); |
| continue; |
| } |
| index++; |
| } |
| return -1; |
| } |
| |
| TreeItem findItem(TreeItem[] items, Point pt) { |
| int start = 0, end = items.length - 1; |
| int index = end / 2; |
| while (end - start > 1) { |
| TreeItem currentItem = items[index]; |
| Rectangle bounds = currentItem.getBounds(); |
| if (pt.y < bounds.y) { |
| end = index; |
| index = (end - start) / 2; |
| } else { |
| start = index; |
| index = start + ((end - start) / 2); |
| } |
| } |
| |
| Rectangle endBounds = items[end].getBounds(); |
| if (endBounds.y < pt.y) { |
| if (endBounds.y + endBounds.height < pt.y) { |
| if (!items[end].getExpanded()) return null; |
| return findItem(items[end].getItems(), pt); |
| } |
| int[] columnOrder = tree.getColumnOrder(); |
| Rectangle bounds = null; |
| if (columnOrder.length > 0) { |
| Rectangle rect1 = items[end].getBounds(columnOrder[0]); |
| Rectangle rect2 = items[end].getBounds(columnOrder[columnOrder.length - 1]); |
| bounds = rect1.union(rect2); |
| bounds.height += tree.getLinesVisible() ? tree.getGridLineWidth() : 0; |
| } else { |
| bounds = items[end].getBounds(); |
| } |
| return bounds.contains(pt) ? items[end] : null; |
| } |
| |
| Rectangle startBounds = items[start].getBounds(); |
| if (startBounds.y + startBounds.height < pt.y) { |
| return findItem(items[start].getItems(), pt); |
| } |
| int[] columnOrder = tree.getColumnOrder(); |
| Rectangle bounds = null; |
| if (columnOrder.length > 0) { |
| Rectangle rect1 = items[start].getBounds(columnOrder[0]); |
| Rectangle rect2 = items[start].getBounds(columnOrder[columnOrder.length - 1]); |
| bounds = rect1.union(rect2); |
| bounds.height += tree.getLinesVisible() ? tree.getGridLineWidth() : 0; |
| } else { |
| bounds = items[start].getBounds(); |
| } |
| return bounds.contains(pt) ? items[start] : null; |
| } |
| |
| /** |
| * Returns the background color that the receiver will use to draw. |
| * |
| * @return the receiver's background color |
| */ |
| @Override |
| public Color getBackground() { |
| checkWidget(); |
| if (background == null) { |
| return getDisplay().getSystemColor(BACKGROUND); |
| } |
| return background; |
| } |
| |
| /** |
| * Returns the index of the column over which the TreeCursor is positioned. |
| * |
| * @return the column index for the current position |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public int getColumn() { |
| checkWidget(); |
| return column == null ? 0 : tree.indexOf(column); |
| } |
| |
| /** |
| * Returns the foreground color that the receiver will use to draw. |
| * |
| * @return the receiver's foreground color |
| */ |
| @Override |
| public Color getForeground() { |
| checkWidget(); |
| if (foreground == null) { |
| return getDisplay().getSystemColor(FOREGROUND); |
| } |
| return foreground; |
| } |
| |
| TreeItem getLastVisibleItem(TreeItem[] items) { |
| if (items == null) return null; |
| TreeItem last = items[items.length - 1]; |
| if (last.getExpanded() && last.getItemCount() > 0) { |
| return getLastVisibleItem(last.getItems()); |
| } |
| return last; |
| } |
| |
| TreeItem getNextItem(TreeItem item) { |
| if (item == null) return null; |
| if (item.getExpanded() && item.getItemCount() > 0) { |
| return item.getItem(0); |
| } |
| |
| TreeItem parentItem = item.getParentItem(); |
| while (parentItem != null) { |
| int index = parentItem.indexOf(item); |
| if (index == -1) return null; |
| if (index < parentItem.getItemCount() - 1) { |
| return parentItem.getItem(index + 1); |
| } |
| item = parentItem; |
| parentItem = item.getParentItem(); |
| } |
| int index = tree.indexOf(item); |
| if (index == -1) return null; |
| if (index == tree.getItemCount() - 1) return null; |
| return tree.getItem(index + 1); |
| } |
| |
| TreeItem getPreviousItem(TreeItem item) { |
| if (item == null) return null; |
| TreeItem parentItem = item.getParentItem(); |
| if (parentItem == null) { |
| int index = tree.indexOf(item); |
| if (index == -1 || index == 0) return null; |
| item = tree.getItem(index - 1); |
| if (item.getExpanded() && item.getItemCount() > 0) { |
| return getLastVisibleItem(item.getItems()); |
| } |
| return item; |
| } |
| int index = parentItem.indexOf(item); |
| if (index == -1) return null; |
| if (index == 0) return parentItem; |
| item = parentItem.getItem(index - 1); |
| if (item.getExpanded() && item.getItemCount() > 0) { |
| return getLastVisibleItem(item.getItems()); |
| } |
| return item; |
| } |
| |
| /** |
| * Returns the row over which the TreeCursor is positioned. |
| * |
| * @return the item for the current position, or <code>null</code> if none |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public TreeItem getRow() { |
| checkWidget(); |
| return row; |
| } |
| |
| void keyDown(Event event) { |
| if (row == null) return; |
| switch (event.character) { |
| case SWT.CR: |
| notifyListeners(SWT.DefaultSelection, new Event()); |
| return; |
| } |
| switch (event.keyCode) { |
| case SWT.ARROW_UP: |
| TreeItem previousItem = getPreviousItem(row); |
| if (previousItem != null) { |
| setRowColumn(previousItem, column, true); |
| } |
| break; |
| case SWT.ARROW_DOWN: |
| TreeItem nextItem = getNextItem(row); |
| if (nextItem != null) { |
| setRowColumn(nextItem, column, true); |
| } |
| break; |
| case SWT.ARROW_LEFT: |
| case SWT.ARROW_RIGHT: { |
| if ((event.stateMask & SWT.MOD1) != 0) { |
| row.setExpanded (event.keyCode == SWT.ARROW_RIGHT); |
| break; |
| } |
| int columnCount = tree.getColumnCount(); |
| if (columnCount == 0) break; |
| int columnIndex = column == null ? 0 : tree.indexOf(column); |
| int[] columnOrder = tree.getColumnOrder(); |
| int index = 0; |
| while (index < columnOrder.length) { |
| if (columnOrder[index] == columnIndex) break; |
| index++; |
| } |
| if (index == columnOrder.length) index = 0; |
| int leadKey = (getStyle() & SWT.RIGHT_TO_LEFT) != 0 ? SWT.ARROW_RIGHT : SWT.ARROW_LEFT; |
| TreeItem parentRow = row.getParentItem(); |
| int rowIndex = tree.indexOf(row); |
| if (event.keyCode == leadKey) { |
| if (parentRow != null) { |
| setRowColumn(row, tree.getColumn(columnOrder[Math.max(0, index - 1)]), true); |
| } else { |
| setRowColumn(rowIndex, columnOrder[Math.max(0, index - 1)], true); |
| } |
| } else { |
| if (parentRow != null) { |
| setRowColumn(row, tree.getColumn(columnOrder[Math.min(columnCount - 1, index + 1)]), true); |
| } else { |
| setRowColumn(rowIndex, columnOrder[Math.min(columnCount - 1, index + 1)], true); |
| } |
| } |
| break; |
| } |
| case SWT.HOME: |
| int columnIndex = column == null ? 0 : tree.indexOf(column); |
| setRowColumn(0, columnIndex, true); |
| break; |
| case SWT.END: { |
| TreeItem[] items = tree.getItems(); |
| setRowColumn(getLastVisibleItem(items), column, true); |
| break; |
| } |
| case SWT.PAGE_UP: { |
| Rectangle rect = tree.getClientArea(); |
| Rectangle itemRect = tree.getTopItem().getBounds(); |
| TreeItem item = row; |
| int index = findIndex(tree.getItems(), item); |
| int itemHeight = tree.getItemHeight(); |
| rect.height -= itemRect.y; |
| int page = Math.max(1, rect.height / itemHeight); |
| if (index - page <= 0) { |
| TreeItem first = tree.getItem(0); |
| setRowColumn(first, column, true); |
| break; |
| } |
| for (int i = 0; i < page; i++) { |
| item = getPreviousItem(item); |
| } |
| setRowColumn(item, column, true); |
| break; |
| } |
| case SWT.PAGE_DOWN: { |
| Rectangle rect = tree.getClientArea(); |
| Rectangle itemRect = tree.getTopItem().getBounds(); |
| TreeItem item = row; |
| int index = findIndex(tree.getItems(), item); |
| int height = tree.getItemHeight(); |
| rect.height -= itemRect.y; |
| TreeItem last = getLastVisibleItem(tree.getItems()); |
| int page = Math.max(1, rect.height / height); |
| int end = findIndex(tree.getItems(), last); |
| if (end <= index + page) { |
| setRowColumn(last, column, true); |
| break; |
| } |
| for (int i = 0; i < page; i++) { |
| item = getNextItem(item); |
| } |
| setRowColumn(item, column, true); |
| break; |
| } |
| } |
| } |
| |
| void onDispose(Event event) { |
| removeListener(SWT.Dispose, listener); |
| notifyListeners(SWT.Dispose, event); |
| event.type = SWT.None; |
| |
| tree.removeListener(SWT.Collapse, treeListener); |
| tree.removeListener(SWT.Expand, treeListener); |
| tree.removeListener(SWT.FocusIn, treeListener); |
| tree.removeListener(SWT.MouseDown, treeListener); |
| unhookRowColumnListeners(); |
| ScrollBar hBar = tree.getHorizontalBar(); |
| if (hBar != null) { |
| hBar.removeListener(SWT.Selection, resizeListener); |
| } |
| ScrollBar vBar = tree.getVerticalBar(); |
| if (vBar != null) { |
| vBar.removeListener(SWT.Selection, resizeListener); |
| } |
| } |
| |
| void paint(Event event) { |
| if (row == null) return; |
| int columnIndex = column == null ? 0 : tree.indexOf(column); |
| int orderedIndex = columnIndex; |
| int[] columnOrder = tree.getColumnOrder(); |
| for (int i = 0; i < columnOrder.length; i++) { |
| if (columnOrder[i] == columnIndex) { |
| orderedIndex = i; |
| break; |
| } |
| } |
| GC gc = event.gc; |
| gc.setBackground(getBackground()); |
| gc.setForeground(getForeground()); |
| gc.fillRectangle(event.x, event.y, event.width, event.height); |
| Image image = row.getImage(columnIndex); |
| int x = 0; |
| // Temporary code - need a better way to determine trim |
| String platform = SWT.getPlatform(); |
| if (image != null) { |
| if ("win32".equals(platform)) { //$NON-NLS-1$ |
| if (orderedIndex > 0) { |
| x += 2; |
| } |
| } else { |
| x += 2; |
| } |
| } |
| Point size = getSize(); |
| if (image != null) { |
| Rectangle imageSize = image.getBounds(); |
| int imageY = (size.y - imageSize.height) / 2; |
| gc.drawImage(image, x, imageY); |
| x += imageSize.width; |
| } |
| String text = row.getText(columnIndex); |
| if (text.length() > 0) { |
| Rectangle bounds = row.getBounds(columnIndex); |
| Point extent = gc.stringExtent(text); |
| // Temporary code - need a better way to determine trim |
| if ("win32".equals(platform)) { //$NON-NLS-1$ |
| if (tree.getColumnCount() == 0 || orderedIndex == 0) { |
| x += image == null ? 2 : 5; |
| } else { |
| int alignmnent = column.getAlignment(); |
| switch (alignmnent) { |
| case SWT.LEFT: |
| x += image == null ? 5 : 3; |
| break; |
| case SWT.RIGHT: |
| x = bounds.width - extent.x - 2; |
| break; |
| case SWT.CENTER: |
| x += Math.ceil((bounds.width - x - extent.x) / 2.0); |
| break; |
| } |
| } |
| } else { |
| if (tree.getColumnCount() == 0) { |
| x += image == null ? 4 : 3; |
| } else { |
| int alignmnent = column.getAlignment(); |
| switch (alignmnent) { |
| case SWT.LEFT: |
| x += image == null ? 5 : 3; |
| break; |
| case SWT.RIGHT: |
| x = bounds.width - extent.x - 2; |
| break; |
| case SWT.CENTER: |
| x += (bounds.width - x - extent.x) / 2 + 2; |
| break; |
| } |
| } |
| } |
| int textY = (size.y - extent.y) / 2; |
| gc.drawString(text, x, textY); |
| } |
| if (isFocusControl()) { |
| Display display = getDisplay(); |
| gc.setBackground(display.getSystemColor(SWT.COLOR_BLACK)); |
| gc.setForeground(display.getSystemColor(SWT.COLOR_WHITE)); |
| gc.drawFocus(0, 0, size.x, size.y); |
| } |
| } |
| |
| /** |
| * Removes the listener from the collection of listeners who will be notified when the |
| * receiver's selection changes. |
| * |
| * @param listener the listener which should no longer be notified |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the listener is null</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| * |
| * @see SelectionListener |
| * @see #addSelectionListener(SelectionListener) |
| */ |
| public void removeSelectionListener(SelectionListener listener) { |
| checkWidget(); |
| if (listener == null) SWT.error(SWT.ERROR_NULL_ARGUMENT); |
| removeListener(SWT.Selection, listener); |
| removeListener(SWT.DefaultSelection, listener); |
| } |
| |
| void _resize() { |
| if (row == null) { |
| setBounds(-200, -200, 0, 0); |
| } else { |
| int columnIndex = column == null ? 0 : tree.indexOf(column); |
| setBounds(row.getBounds(columnIndex)); |
| } |
| } |
| |
| /** |
| * Sets the receiver's background color to the color specified |
| * by the argument, or to the default system color for the control |
| * if the argument is null. |
| * <p> |
| * Note: This operation is a hint and may be overridden by the platform. |
| * For example, on Windows the background of a Button cannot be changed. |
| * </p> |
| * @param color the new color (or null) |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| @Override |
| public void setBackground (Color color) { |
| background = color; |
| super.setBackground(getBackground()); |
| redraw(); |
| } |
| /** |
| * Sets the receiver's foreground color to the color specified |
| * by the argument, or to the default system color for the control |
| * if the argument is null. |
| * <p> |
| * Note: This operation is a hint and may be overridden by the platform. |
| * </p> |
| * @param color the new color (or null) |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the argument has been disposed</li> |
| * </ul> |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| @Override |
| public void setForeground (Color color) { |
| foreground = color; |
| super.setForeground(getForeground()); |
| redraw(); |
| } |
| |
| void setRowColumn(int row, int column, boolean notify) { |
| TreeItem item = row == -1 ? null : tree.getItem(row); |
| TreeColumn col = column == -1 || tree.getColumnCount() == 0 ? null : tree.getColumn(column); |
| setRowColumn(item, col, notify); |
| } |
| |
| void setRowColumn(TreeItem row, TreeColumn column, boolean notify) { |
| if (this.row != null && this.row != row) { |
| TreeItem currentItem = this.row; |
| while (currentItem != null) { |
| currentItem.removeListener(SWT.Dispose, disposeItemListener); |
| currentItem = currentItem.getParentItem(); |
| } |
| this.row = null; |
| } |
| if (this.column != null && this.column != column) { |
| this.column.removeListener(SWT.Dispose, disposeColumnListener); |
| this.column.removeListener(SWT.Move, resizeListener); |
| this.column.removeListener(SWT.Resize, resizeListener); |
| this.column = null; |
| } |
| if (row != null) { |
| if (this.row != row) { |
| this.row = row; |
| TreeItem currentItem = row; |
| while (currentItem != null) { |
| currentItem.addListener(SWT.Dispose, disposeItemListener); |
| currentItem = currentItem.getParentItem(); |
| } |
| tree.showItem(row); |
| } |
| if (this.column != column && column != null) { |
| this.column = column; |
| column.addListener(SWT.Dispose, disposeColumnListener); |
| column.addListener(SWT.Move, resizeListener); |
| column.addListener(SWT.Resize, resizeListener); |
| tree.showColumn(column); |
| } |
| int columnIndex = column == null ? 0 : tree.indexOf(column); |
| setBounds(row.getBounds(columnIndex)); |
| redraw(); |
| if (notify) notifyListeners(SWT.Selection, new Event()); |
| } |
| } |
| |
| /** |
| * Positions the TreeCursor over the root-level cell at the given row and column in the parent tree. |
| * |
| * @param row the index of the root-level row for the cell to select |
| * @param column the index of column for the cell to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void setSelection(int row, int column) { |
| checkWidget(); |
| int columnCount = tree.getColumnCount(); |
| int maxColumnIndex = columnCount == 0 ? 0 : columnCount - 1; |
| if (row < 0 || row >= tree.getItemCount() || column < 0 || column > maxColumnIndex) { |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| } |
| setRowColumn(row, column, false); |
| } |
| |
| /** |
| * Positions the TreeCursor over the cell at the given row and column in the parent tree. |
| * |
| * @param row the TreeItem of the row for the cell to select |
| * @param column the index of column for the cell to select |
| * |
| * @exception SWTException <ul> |
| * <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li> |
| * <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li> |
| * </ul> |
| */ |
| public void setSelection(TreeItem row, int column) { |
| checkWidget(); |
| int columnCount = tree.getColumnCount(); |
| int maxColumnIndex = columnCount == 0 ? 0 : columnCount - 1; |
| if (row == null || row.isDisposed() || column < 0 || column > maxColumnIndex) { |
| SWT.error(SWT.ERROR_INVALID_ARGUMENT); |
| } |
| TreeColumn col = tree.getColumnCount() == 0 ? null : tree.getColumn(column); |
| setRowColumn(row, col, false); |
| } |
| |
| @Override |
| public void setVisible(boolean visible) { |
| checkWidget(); |
| if (visible) { |
| _resize(); |
| } |
| super.setVisible(visible); |
| } |
| |
| void treeCollapse(Event event) { |
| if (row == null) return; |
| TreeItem root = (TreeItem)event.item; |
| TreeItem parentItem = row.getParentItem(); |
| while (parentItem != null) { |
| if (parentItem == root) { |
| setRowColumn(root, column, true); |
| return; |
| } |
| parentItem = parentItem.getParentItem(); |
| } |
| |
| getDisplay().asyncExec(() -> { |
| if (isDisposed()) return; |
| setRowColumn(row, column, true); |
| }); |
| } |
| |
| void treeExpand(Event event) { |
| getDisplay().asyncExec(() -> { |
| if (isDisposed()) return; |
| setRowColumn(row, column, true); |
| }); |
| } |
| |
| void treeFocusIn(Event event) { |
| if (isVisible()) { |
| if (row == null && column == null) return; |
| setFocus(); |
| } |
| } |
| |
| void treeMouseDown(Event event) { |
| if (tree.getItemCount() == 0) return; |
| Point pt = new Point(event.x, event.y); |
| TreeItem item = tree.getItem(pt); |
| if (item == null && (tree.getStyle() & SWT.FULL_SELECTION) == 0) { |
| TreeItem currentItem = tree.getTopItem(); |
| TreeItem parentItem = currentItem.getParentItem(); |
| while (parentItem != null) { |
| currentItem = parentItem; |
| parentItem = currentItem.getParentItem(); |
| } |
| int start = tree.indexOf(currentItem); |
| int viewportItemCount = tree.getClientArea().height / tree.getItemHeight(); |
| int end = Math.min(start + viewportItemCount, tree.getItemCount() - 1); |
| TreeItem[] allItems = tree.getItems(); |
| TreeItem[] items = new TreeItem[end - start + 1]; |
| System.arraycopy(allItems, start, items, 0, end - start + 1); |
| item = findItem(items, pt); |
| } |
| if (item == null) return; |
| |
| TreeColumn newColumn = null; |
| int lineWidth = tree.getLinesVisible() ? tree.getGridLineWidth() : 0; |
| int columnCount = tree.getColumnCount(); |
| if (columnCount > 0) { |
| for (int i = 0; i < columnCount; i++) { |
| Rectangle rect = item.getBounds(i); |
| rect.width += lineWidth; |
| rect.height += lineWidth; |
| if (rect.contains(pt)) { |
| newColumn = tree.getColumn(i); |
| break; |
| } |
| } |
| if (newColumn == null) { |
| newColumn = tree.getColumn(0); |
| } |
| } |
| setRowColumn(item, newColumn, true); |
| setFocus(); |
| } |
| |
| void unhookRowColumnListeners() { |
| if (column != null && !column.isDisposed()) { |
| column.removeListener(SWT.Dispose, disposeColumnListener); |
| column.removeListener(SWT.Move, resizeListener); |
| column.removeListener(SWT.Resize, resizeListener); |
| } |
| column = null; |
| if (row != null && !row.isDisposed()) { |
| TreeItem currentItem = row; |
| while (currentItem != null) { |
| currentItem.removeListener(SWT.Dispose, disposeItemListener); |
| currentItem = currentItem.getParentItem(); |
| } |
| } |
| row = null; |
| } |
| |
| } |