blob: aacccbe55dc9f3e4af76627d4eb3e891480bec95 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2012 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 = new Listener() {
public void handleEvent(Event 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 = new Listener() {
public void handleEvent(Event 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 = new Listener() {
public void handleEvent(Event 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 = new Listener() {
public void handleEvent(Event 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 = new Listener() {
public void handleEvent(Event 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() {
public void getRole(AccessibleControlEvent e) {
e.detail = ACC.ROLE_TABLECELL;
}
});
getAccessible().addAccessibleListener(new AccessibleAdapter() {
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
*/
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
*/
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>
*/
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>
*/
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);
}
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(new Runnable() {
public void run() {
if (isDisposed()) return;
setRowColumn(row, column, true);
}
});
}
void treeExpand(Event event) {
getDisplay().asyncExec(new Runnable() {
public void run() {
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;
}
}