blob: e19494cdd073842ae7224d2b5c271f0ebae152d4 [file] [log] [blame]
package org.eclipse.swt.widgets;
/*
* (c) Copyright IBM Corp. 2000, 2001.
* All Rights Reserved
*/
import org.eclipse.swt.*;
import org.eclipse.swt.graphics.*;
import java.util.*;
/**
* Instances of this class represent a selectable user interface object
* that represents a hierarchy of tree items in a tree widget.
*
* <dl>
* <dt><b>Styles:</b></dt>
* <dd>(none)</dd>
* <dt><b>Events:</b></dt>
* <dd>(none)</dd>
* </dl>
* <p>
* IMPORTANT: This class is <em>not</em> intended to be subclassed.
* </p>
*/
public /*final*/ class TreeItem extends AbstractTreeItem {
/*
* This class caches geometric data for drawing.
* A description of the cached data follows:
*
* | 1 || 5 |
* | 2 | | 6 |
* |3 7|
* _____ | 4 |f| |8
* | | ____
* | - | ===== {image} root 9
* |_____| |
* |b|c| |d|
* | e |
*
* Widths are measured between vertical lines.
*
* Cached item rendering data:
* 1 = getDecorationsWidth
* 2 = getHierarchyIndicatorRect
* 3 = getPaintStartX
* 4 = getItemConnectorWidth
* 5 = getItemWidth
* 6 = getSelectionWidth
* 7 = getPaintStopX
* 8 - getTextXPos
* 9 = getTextYPosition
*
* Rendering constants:
* 4 = DEFAULT_ITEM_CONNECTOR_WIDTH, used when no image is set in the tree.
* Otherwise it is the image width.
* b = IMAGE_PADDING
* c = TEXT_INDENT
* d = SELECTION_PADDING
* e = ITEM_NOIMAGE_OFFSET
* f = ITEM_CONNECTOR_PADDING;
*/
private static final int DEFAULT_ITEM_CONNECTOR_WIDTH = 8; // Default width of the horizontal line connecting
// items with the vertical lines. Only used when
// no image is set in the tree. Normally connector
// line width is half the image width.
private static final int ITEM_CONNECTOR_PADDING = 2; // Added to the calculated item connector width
private static final int IMAGE_PADDING = 3; // Space behind bitmap
private static final int ITEM_NOIMAGE_OFFSET = 8; // Offset added to the calculated paint position where
// an item starts drawing. To be used when no item
// image has been set. Otherwise children would start
// drawing at the end of the horizontal item connector
// of their parent.
private static final int ROOT_INDENT = 5; // Indent of root items
private static final int SELECTION_PADDING = 2; // Space behind text
private static final int TEXT_INDENT = 2; // Identation of the item label
// basic item info
private TreeItem parentItem;
private int index; // index in the parent item
// geometrical item info
private int paintStartX = -1; // X coordinate of the upper-left corner of the
// receivers bounding rectangle
private Point itemExtent; // Size of the item (image + label)
private Point imageExtent; // original size of the item image
private int textYPosition = -1; // Centered y position of the item text
/**
* Create a root item and add it to the tree widget identified
* by 'parent'.
* @param parent - Tree widget the receiver is added to
* @param swtStyle - widget style. see Widget class for details
*/
public TreeItem(Tree parent, int swtStyle) {
this(parent, swtStyle, checkNull(parent).getItemCount());
}
/**
* Create a root item and add it to the tree widget identified
* by 'parent'.
* @param parent - Tree widget the receiver is added to.
* @param swtStyle - widget style. see Widget class for details
* @param position - position in 'parentItem' the receiver will
* be inserted at
*/
public TreeItem(Tree parent, int swtStyle, int position) {
super(parent, swtStyle);
parent.addItem(this, position);
}
/**
* Create a root item with 'parentItem' as the parent item.
* @param parentItem - the parent item of the receiver
* @param swtStyle - widget style. see Widget class for details
*/
public TreeItem(TreeItem parentItem, int swtStyle) {
this(parentItem, swtStyle, checkNull(parentItem).getItemCount());
}
/**
* Create a root item with 'parentItem' as the parent item.
* @param parentItem - the parent item of the receiver
* @param swtStyle - widget style. see Widget class for details
* @param position - position in 'parentItem' the receiver will
* be inserted at
*/
public TreeItem(TreeItem parentItem, int swtStyle, int position) {
super(checkNull(parentItem).getParent(), swtStyle);
setParentItem(parentItem);
parentItem.add(this, position);
}
/**
* Calculate the number of expanded children.
* Recurse up in the tree to the root item.
*/
void calculateVisibleItemCount() {
Vector children;
TreeItem child;
int visibleItemCount = 0;
// check isExpanded field directly for performance
if (internalGetExpanded() == true) {
children = getChildren();
visibleItemCount = children.size();
for (int i = 0; i < children.size(); i++) {
child = (TreeItem) children.elementAt(i);
visibleItemCount += child.getVisibleItemCount();
}
}
setVisibleItemCount(visibleItemCount);
calculateVisibleItemCountParent();
}
/**
* Calculate the number of expanded children for the parent item
* of this item.
*/
void calculateVisibleItemCountParent() {
TreeItem parentItem = getParentItem();
if (parentItem != null) {
parentItem.calculateVisibleItemCount();
}
else {
getParent().getRoot().calculateVisibleItemCount();
}
}
/**
* Throw an SWT.ERROR_NULL_ARGUMENT exception if 'tree' is null.
* Otherwise return 'tree'
*/
static Tree checkNull(Tree tree) {
if (tree == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
return tree;
}
/**
* Throw an SWT.ERROR_NULL_ARGUMENT exception if 'item' is null.
* Otherwise return 'item'
*/
static TreeItem checkNull(TreeItem item) {
if (item == null) SWT.error(SWT.ERROR_NULL_ARGUMENT);
return item;
}
protected void checkSubclass () {
if (!isValidSubclass ()) error (SWT.ERROR_INVALID_SUBCLASS);
}
/**
* Draw the hierarchy indicator at 'position'.
*
* Note:
* Assumes that the hierarchy indicators for the expanded and
* collapsed state are the same size.
* @param gc - GC to draw on.
* @param position - position on the GC to draw at.
* @return position to continue drawing
*/
Point drawHierarchyIndicator(GC gc, Point position) {
Tree parent = getParent();
Image hierarchyImage;
Rectangle indicatorRectangle = parent.getHierarchyIndicatorRect();
int x = position.x;
int y = position.y;
int yCenter = y + parent.getItemHeight() / 2;
Point connectorLinePosition;
if (isLeaf() == false) {
if (getExpanded() == true) {
hierarchyImage = parent.getExpandedImage();
}
else {
hierarchyImage = parent.getCollapsedImage();
}
if (hierarchyImage != null) {
gc.drawImage(hierarchyImage, x + indicatorRectangle.x, y + indicatorRectangle.y);
}
connectorLinePosition = new Point(x + indicatorRectangle.width, yCenter);
}
else {
connectorLinePosition = new Point(
x + indicatorRectangle.width / 2
+ indicatorRectangle.width % 2, yCenter); // % 2 in order to not start the next hierarchy
// component at the middle of the icon but after.
}
return connectorLinePosition;
}
/**
* Draw a horizontal line connecting the item image (or label
* if there is no image) to the vertical line connecting to
* the parent.
* @param gc - GC to draw on.
* @param position - position on the GC to draw at.
* @return position to continue drawing
*/
Point drawHorizontalItemConnector(GC gc, Point position) {
int itemConnectorEndPos = position.x + getItemConnectorWidth() - 1; // -1 because the position of the last pixel needs to be calculated
gc.drawLine(position.x, position.y, itemConnectorEndPos, position.y);
return new Point(itemConnectorEndPos + 1, position.y); // + 1 in order to resume drawing after line not on end of line
}
/**
* Display the item image at 'position' using 'gc'.
* @param gc - GC to draw on
* @param position - position on the GC to draw at
* @return position to continue drawing
*/
Point drawImage(GC gc, Point destinationPosition) {
Tree parent = getParent();
Image image = getImage();
Point sourceImageExtent;
Point destinationImageExtent = parent.getImageExtent();
int yCenter;
if (image != null) {
sourceImageExtent = getImageExtent();
yCenter = (parent.getItemHeight() - destinationImageExtent.y) / 2;
gc.drawImage(
image,
0, 0, // source x, y
sourceImageExtent.x, sourceImageExtent.y, // source width, height
destinationPosition.x, destinationPosition.y + yCenter, // destination x, y
destinationImageExtent.x, destinationImageExtent.y); // destination width, height
}
if (destinationImageExtent != null) {
destinationPosition.x += destinationImageExtent.x + IMAGE_PADDING;
}
return destinationPosition;
}
/**
* Draw a filled rectangle indicating the item selection state
* The rectangle will be filled with the selection color if the
* receiver is selected. Otherwise the rectangle will be filled
* in the background color to remove the selection.
* @param gc - GC to draw on.
* @param position - position on the GC to draw at.
*/
void drawSelection(GC gc, Point position) {
Tree parent = getParent();
Point selectionExtent = getSelectionExtent();
if (selectionExtent == null) {
return;
}
if (isSelected() == true) {
gc.setBackground(getSelectionBackgroundColor());
}
gc.fillRectangle(position.x, position.y, selectionExtent.x, selectionExtent.y);
if (isSelected() == true) {
gc.setBackground(parent.getBackground());
}
}
/**
* Draw a rectangle enclosing the item label. The rectangle
* indicates that the receiver was selected last and that it has
* the input focus.
* The rectangle will only be drawn if the receiver is selected.
* @param gc - GC to draw on.
* @param position - position on the GC to draw at.
*/
void drawSelectionFocus(GC gc, Point position) {
Point selectionExtent = getSelectionExtent();
if (selectionExtent == null) {
return;
}
if (getParent().hasFocus(this) == true) {
gc.drawFocus(
position.x, position.y,
selectionExtent.x, selectionExtent.y);
}
}
/**
* Draw the item label at 'position' using 'gc'.
* @param gc - GC to draw on.
* @param position - position on the GC to draw at.
*/
void drawText(GC gc, Point position) {
Tree parent = getParent();
String text = getText();
if (text != null) {
if (isSelected() == true) {
gc.setBackground(getSelectionBackgroundColor());
gc.setForeground(getSelectionForegroundColor());
}
gc.drawString(text, position.x, position.y);
if (isSelected() == true) {
gc.setBackground(parent.getBackground());
gc.setForeground(parent.getForeground());
}
}
}
/**
* Draw a vertical line connecting the horizontal connector line
* with that of the previous item.
* Called recursively to draw the lines on all tree levels.
* @param gc - GC to draw on.
* @param yPosition - y position of the upper side of the
* receiver's bounding box.
* @param isFirstChild - method is called to draw a vertical
* line for the first child. Leave room for the hierarchy icon.
*/
void drawVerticalItemConnector(GC gc, int yPosition, boolean isFirstChild) {
Tree parent = getParent();
TreeItem nextDrawItem = getParentItem();
AbstractTreeItem parentItem = nextDrawItem;
Rectangle indicatorRectangle = parent.getHierarchyIndicatorRect();
int itemHeight = parent.getItemHeight();
int itemHeightDiv2 = itemHeight / 2 + itemHeight % 2;
int indicatorHeightDiv2 = indicatorRectangle.height / 2 + indicatorRectangle.height % 2;
int lineX = getPaintStartX() + indicatorRectangle.width / 2;
int lineStartY = yPosition - itemHeightDiv2;
int lineEndY = yPosition + itemHeightDiv2;
if (parentItem == null) {
parentItem = parent.getRoot();
}
if (getIndex() != parentItem.getItemCount()-1) { // if item is not the last child
if (isFirstChild == true) {
lineStartY += indicatorHeightDiv2; // leave space for the hierarchy image
}
gc.drawLine(lineX, lineStartY, lineX, lineEndY);
}
if (nextDrawItem != null) {
nextDrawItem.drawVerticalItemConnector(gc, yPosition, false);
}
}
/**
* Draw a vertical line connecting the horizontal connector line
* with that of the previous item.
* Do this on all tree levels up to the root level.
* @param gc - GC to draw on.
* @param position - position on the GC to draw at.
* @return position to continue drawing
*/
Point drawVerticalItemConnector(GC gc, Point position) {
Tree parent = getParent();
TreeItem parentItem = getParentItem();
Rectangle indicatorRectangle = parent.getHierarchyIndicatorRect();
int itemHeight = parent.getItemHeight();
int itemHeightDiv2 = itemHeight / 2 + itemHeight % 2;
int indicatorHeightDiv2 = indicatorRectangle.height / 2 + indicatorRectangle.height % 2;
int lineX = position.x + indicatorRectangle.width / 2;
int lineStartY = position.y - itemHeightDiv2;
int lineEndY = position.y + itemHeightDiv2 - itemHeight % 2;
TreeItem predecessor;
boolean isFirstChild = false;
if (isRoot() == true) {
if (getIndex() == 0) {
return position; // first root, don't draw vertical line
}
}
else
if (getIndex() == 0) { // if item is first child
lineStartY += itemHeightDiv2;
isFirstChild = true;
}
predecessor = getPredecessor();
if (predecessor != null && predecessor.isLeaf() == false) {
lineStartY += indicatorHeightDiv2; // leave space for the hierarchy image
}
if (isLeaf() == false) {
lineEndY -= indicatorHeightDiv2;
}
gc.drawLine(lineX, lineStartY, lineX, lineEndY);
if (parentItem != null) {
parentItem.drawVerticalItemConnector(gc, position.y, isFirstChild);
}
return position;
}
/**
* Returns a rectangle describing the receiver's size and location
* relative to its parent.
*
* @return the receiver's bounding rectangle
*
* @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 Rectangle getBounds() {
if (!isValidThread()) error(SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget()) error(SWT.ERROR_WIDGET_DISPOSED);
Tree parent = getParent();
Point extent = getItemExtent();
int x = getTextXPos() - TEXT_INDENT;
return new Rectangle(x, parent.getRedrawY(this), extent.x - (x - getItemStartX()), extent.y);
}
/**
* Answer the x position of the item check box
*/
int getCheckboxXPosition() {
return getPaintStartX() + getDecorationsWidth();
}
/**
* Answer the combined width of the hierarchy indicator and
* the horizontal item connector line.
*/
int getDecorationsWidth() {
int indicatorWidth = getParent().getHierarchyIndicatorRect().width;
int width = indicatorWidth + getItemConnectorWidth();
if (isLeaf() == true) {
width -= indicatorWidth / 2;
}
return width;
}
/**
* Answer the index of the receiver relative to the first root
* item.
* @return
* The index of the receiver relative to the first root item.
*/
int getGlobalIndex() {
int globalItemIndex = getIndex();
AbstractTreeItem item = null;
if (isRoot() == false) {
item = getParentItem();
globalItemIndex++; // adjust for 0-based non-root items
}
else {
item = getParent().getRoot();
}
globalItemIndex += item.getVisibleIndex(getIndex());
return globalItemIndex;
}
/**
* Answer the original size of the image of the receiver.
*/
Point getImageExtent() {
Image image = getImage();
Rectangle imageBounds;
if (imageExtent == null && image != null) {
imageBounds = image.getBounds();
imageExtent = new Point(imageBounds.width, imageBounds.height);
}
return imageExtent;
}
/**
* Answer the receiver's index into its parent's list of children
*/
int getIndex() {
return index;
}
/**
* Answer the width of the horizontal item connector line.
*/
int getItemConnectorWidth() {
Tree parent = getParent();
Point imageExtent = parent.getImageExtent();
int itemConnectorWidth;
int indicatorWidth = parent.getHierarchyIndicatorRect().width;
if (imageExtent != null) {
itemConnectorWidth = imageExtent.x / 2 + ITEM_CONNECTOR_PADDING;
}
else {
itemConnectorWidth = DEFAULT_ITEM_CONNECTOR_WIDTH;
}
if (isLeaf() == false) { // has children = has hierarchy indicator = shorter connector
itemConnectorWidth -= indicatorWidth / 2;
}
return itemConnectorWidth;
}
/**
* Returns the number of items contained in the receiver
* that are direct item children of the receiver.
*
* @return the number of items
*
* @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 getItemCount() {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
return super.getItemCount();
}
/**
* Answer the size of the receiver as displayed on the screen.
*/
Point getItemExtent() {
Tree parent;
Point imageExtent;
String text;
int itemWidth;
if (itemExtent == null) {
parent = getParent();
imageExtent = parent.getImageExtent();
text = getText();
itemWidth = SELECTION_PADDING;
if (text != null) {
itemWidth += parent.getTextWidth(text) + TEXT_INDENT;
}
if (imageExtent != null) {
itemWidth += imageExtent.x + IMAGE_PADDING;
}
itemExtent = new Point(itemWidth, parent.getItemHeight());
}
return itemExtent;
}
/**
* Answer the x position at which painting of the receiver's
* contents (ie. image, text) can begin.
*/
int getItemStartX() {
int itemStartX = getPaintStartX() + getDecorationsWidth();
if (isCheckable() == true) {
itemStartX += getCheckboxBounds().width + CHECKBOX_PADDING;
}
return itemStartX;
}
/**
* Returns an array of <code>TreeItem</code>s which are the
* direct item children of the receiver.
* <p>
* Note: This is not the actual structure used by the receiver
* to maintain its list of items, so modifying the array will
* not affect the receiver.
* </p>
*
* @return the receiver's items
*
* @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 [] getItems() {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
TreeItem childrenArray[] = new TreeItem[getItemCount()];
getChildren().copyInto(childrenArray);
return childrenArray;
}
/**
* Answer the x position where the receiver is drawn.
*/
int getPaintStartX() {
Tree parent = getParent();
Point imageExtent;
TreeItem parentItem;
if (paintStartX == -1) {
if (isRoot() == true) {
paintStartX = ROOT_INDENT;
}
else {
parentItem = getParentItem();
// subtract parent.getHorizontalOffset() to calculate the cached start
// position independent of the horizontal scroll offset. Fixes 1G1L7EU.
paintStartX = parentItem.getPaintStartX()
- parent.getHorizontalOffset()
+ parentItem.getDecorationsWidth()
- parent.getHierarchyIndicatorRect().width / 2;
imageExtent = parent.getImageExtent();
if (imageExtent != null) {
paintStartX += imageExtent.x / 2;
}
else {
paintStartX += ITEM_NOIMAGE_OFFSET;
}
}
}
return paintStartX + parent.getHorizontalOffset();
}
/**
* Answer the pixel at which the receiver stops drawing.
*/
int getPaintStopX() {
return (getItemStartX() + getItemExtent().x - getParent().getHorizontalOffset());
}
/**
* Returns the receiver's parent, which must be a <code>Tree</code>.
*
* @return the receiver's parent
*
* @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 Tree getParent() {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
return (Tree) super.getSelectableParent();
}
/**
* Returns the receiver's parent item, which must be a
* <code>TreeItem</code> or null when the receiver is a
* root.
*
* @return the receiver's parent item
*
* @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 getParentItem() {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
return parentItem;
}
/**
* Answer the item that directly precedes the receiver.
* Answer null if this is the first item in a hierarchy level
* or if there are expanded children in the previous item.
*/
TreeItem getPredecessor() {
AbstractTreeItem parentItem = getParentItem();
Vector children;
int previousIndex = getIndex() - 1;
TreeItem previousItem = null;
if (parentItem == null) {
parentItem = getParent().getRoot();
}
if (previousIndex >= 0) {
children = parentItem.getChildren();
previousItem = (TreeItem) children.elementAt(previousIndex);
if (previousItem.isLeaf() == false && previousItem.getExpanded() == true) {
previousItem = null; // no immediate predecessor because there are expanded children
}
}
return previousItem;
}
/**
* Answer the size of the rectangle drawn to indicate the
* selected state of the receiver.
* This is also used to draw the selection focus rectangle.
*/
Point getSelectionExtent() {
Point selectionExtent = getItemExtent();
Point imageExtent = getParent().getImageExtent();
int x = selectionExtent.x;
if (imageExtent != null) {
x -= imageExtent.x + IMAGE_PADDING;
}
return new Point(x, selectionExtent.y);
}
/**
* Return the x position of the selection rectangle
*/
int getSelectionX() {
return getTextXPos() - TEXT_INDENT;
}
/**
* Answer the x position where the receiver draws the item text.
* This position is relative to the item start position.
*/
int getTextXPos() {
Point imageExtent = getParent().getImageExtent();
int textXPos = getItemStartX() + TEXT_INDENT;
if (imageExtent != null) {
textXPos += imageExtent.x + IMAGE_PADDING;
}
return textXPos;
}
/**
* Answer the y position of the receiver's text.
* @param
* gc - GC to use for calculating the text y position
*/
int getTextYPosition(GC gc) {
String text;
if (textYPosition == -1) {
text = getText();
if (text != null) {
textYPosition = (getParent().getItemHeight() - gc.stringExtent(text).y) / 2;
}
else {
textYPosition = 0;
}
}
return textYPosition;
}
/**
* Answer the index of the receiver relative to the first root
* item.
* If 'anIndex' is the index of the expanded item 'anItem'
* then the following expressions are true:
* 'anItem == theRoot.getVisibleItem(anIndex)' and
* 'anIndex == anItem.getVisibleIndex()'
* @return
* The index of the receiver relative to the first root item.
* Answer -1 if the receiver is not visible (because the parent
* is collapsed).
*/
int getVisibleIndex() {
int visibleItemIndex = getIndex();
AbstractTreeItem item = null;
if (isRoot() == false) {
if (isVisible() == false) {
return -1;
}
item = getParentItem();
visibleItemIndex++; // adjust for 0-based non-root items
}
else {
item = getParent().getRoot();
}
visibleItemIndex += item.getVisibleIndex(getIndex());
return visibleItemIndex;
}
/**
* Answer the index of the child item identified by 'childIndex'
* relative to the first root item.
*/
int getVisibleIndex(int childIndex) {
Enumeration children = getChildren().elements();
TreeItem child;
int visibleItemIndex = getIndex();
if (isRoot() == false) {
visibleItemIndex++; // adjust for 0-based non-root items
}
while (children.hasMoreElements() == true) {
child = (TreeItem) children.nextElement();
if (child.getIndex() == childIndex) {
if (isRoot() == false) {
visibleItemIndex += getParentItem().getVisibleIndex(getIndex());
}
else {
visibleItemIndex += getParent().getRoot().getVisibleIndex(getIndex());
}
break;
}
visibleItemIndex += child.getVisibleItemCount();
}
return visibleItemIndex;
}
/**
* Answer the item at 'searchIndex' relativ to the receiver.
* When this method is called for the root item, 'searchIndex'
* represents the global index into all items of the tree.
* searchIndex=0 returns the receiver.
* searchIndex=1 returns the first visible child.
* Note: searchIndex must be >= 0
*
* Note:
* Visible in this context does not neccessarily mean that the
* item is displayed on the screen. Visible here means that all
* the parents of the item are expanded. An item is only
* visible on screen if it is within the widget client area.
*/
TreeItem getVisibleItem(int searchIndex) {
TreeItem child;
TreeItem foundItem = null;
Enumeration children = getChildren().elements();
if (searchIndex == 0) {
return this;
}
else
if (getExpanded() == false) { // trying to find a child when this item isn't expanded ?
return null;
}
// Search for expanded items first. Count all subitems in the process.
while (children.hasMoreElements() == true && foundItem == null) {
child = (TreeItem) children.nextElement();
searchIndex--;
if (child.getExpanded() == true) {
searchIndex -= child.getVisibleItemCount(); // count children of all expanded items
}
if (searchIndex <= 0) { // is searched item past child ?
// add back children of current item (that's what we want to search)
foundItem = child.getVisibleItem(searchIndex + child.getVisibleItemCount());
}
}
return foundItem;
}
/**
* Answer whether 'item' is a child, direct or indirect, of the receiver.
* It is an indirect child if it is a child of one of the receiver's children.
*/
boolean isChild(TreeItem item) {
Vector children = getChildren();
TreeItem child;
if (children.contains(item) == true) {
return true;
}
for (int i = 0; i < children.size(); i++) {
child = (TreeItem) children.elementAt(i);
if (child.isChild(item) == true) {
return true;
}
}
return false;
}
/**
* Answer whether the receiver is a root item.
* The receiver is a root item when it does not have a parent item.
* @return
* true - the receiver is a root item.
* false - the receiver is not a root item.
*/
boolean isRoot() {
return (getParentItem() == null);
}
/**
* Answer whether the click at 'position' on the receiver is a selection
* click.
* @param position - location of the mouse click relative to the
* upper left corner of the receiver.
* @return true - receiver was clicked.
* false - receiver was not clicked.
*/
boolean isSelectionHit(Point position) {
Point itemExtent = getItemExtent();
if (itemExtent == null) { // neither image nor text have been set
return false;
}
return (new Rectangle(
getItemStartX() - getPaintStartX(), 0,
itemExtent.x, itemExtent.y)).contains(position);
}
/**
* Answer whether the receiver is visible
* An item is visible when its parent item is visible and
* expanded. Root items are always visible.
*
* Note:
* Visible in this context does not neccessarily mean that the
* item is displayed on the screen. Visible here means that all
* the parents of the item are expanded. An item is only
* visible on screen if it is within the receiver's parent's
* client area.
* @return
* true - the receiver is visible
* false - the receiver is not visible
*/
boolean isVisible() {
boolean isVisible = true;
TreeItem parentItem = getParentItem();
if (isRoot() == false) {
isVisible = parentItem.getExpanded();
if (isVisible == true) {
isVisible = parentItem.isVisible();
}
}
return isVisible;
}
/**
* Make this item visible by expanding its parent item.
*/
void makeVisible() {
TreeItem parentItem = getParentItem();
if (isVisible() == false && parentItem != null) {
getParent().expand(parentItem, true); // have to call Tree.expand directly in order to trigger Expand event
parentItem.makeVisible();
}
}
/**
* Draw the receiver at 'yPosition' in the client area of the parent.
* @param gc - GC to draw on.
* @param yPosition - y coordinate where the receiver should draw at.
*/
void paint(GC gc, int yPosition) {
Tree parent = getParent();
Point paintPosition = new Point(getPaintStartX(), yPosition);
if (isVisible() == false) {
return;
}
gc.setForeground(parent.CONNECTOR_LINE_COLOR);
paintPosition = drawVerticalItemConnector(gc, paintPosition);
paintPosition = drawHierarchyIndicator(gc, paintPosition);
paintPosition = drawHorizontalItemConnector(gc, paintPosition);
gc.setForeground(parent.getForeground());
// paint the rest
if (isCheckable() == true) {
paintPosition = drawCheckbox(gc, new Point(paintPosition.x, yPosition));
}
paintPosition = drawImage(gc, new Point(paintPosition.x, yPosition));
drawSelection(gc, paintPosition);
if (this == parent.getInsertItem()) {
drawInsertMark(gc, paintPosition);
}
drawText(gc, new Point(getTextXPos(), paintPosition.y + getTextYPosition(gc)));
drawSelectionFocus(gc, paintPosition);
}
/**
* Update the display to reflect the expanded state of the
* receiver.
* @param itemIndex - index position in the receiver's client
* area where should be drawn.
*/
void redrawExpanded(int itemIndex) {
Tree parent = getParent();
int indicatorWidth = parent.getHierarchyIndicatorRect().width;
int itemHeight = parent.getItemHeight();
parent.redraw(
getPaintStartX(), itemIndex * itemHeight,
indicatorWidth, itemHeight, false);
}
/**
* Reset cached size and position data.
*/
void reset() {
super.reset();
setImageExtent(null);
setItemExtent(null);
setPaintStartX(-1);
setTextYPosition(-1);
}
/**
* Sets the expanded state of the receiver.
* <p>
*
* @param expanded the new expanded state
*
* @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 setExpanded(boolean expand) {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
if (isLeaf() == false && expand == true) {
getParent().expand(this, false);
}
else {
getParent().collapse(this, false);
}
}
public void setImage(Image newImage) {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
Tree parent = getParent();
Image oldImage = getImage();
boolean isSameImage;
int imageWidth = 0;
int redrawX = 0;
super.setImage(newImage);
if (newImage != null && oldImage != null) {
isSameImage = newImage.equals(oldImage);
}
else {
isSameImage = newImage == oldImage;
}
if (isSameImage == false) {
if (parent.getVisibleRedrawY(this) != -1) {
if (parent.getImageExtent() != null) {
imageWidth = parent.getImageExtent().x;
}
else
if (newImage != null) {
imageWidth = newImage.getBounds().x;
}
redrawX = getItemStartX();
}
parent.itemChanged(this, redrawX, imageWidth);
}
}
/**
* Set the size of the original image of the receiver to 'imageExtent'.
*/
void setImageExtent(Point imageExtent) {
this.imageExtent = imageExtent;
}
/**
* Set the index of the receiver to 'index'.
* This index is used to reference children in their parent.
*/
void setIndex(int index) {
this.index = index;
}
/**
* Set the size of the receiver to 'extent'.
*/
void setItemExtent(Point extent) {
itemExtent = extent;
}
/**
* Set the x position where the receiver is drawn to 'startX'.
* @param startX - the x position where the receiver is drawn
*/
void setPaintStartX(int startX) {
paintStartX = startX;
}
/**
* Set the parent item of the receiver to 'parentItem'.
* @param parentItem - the receiver's parent item.
* Receiver is a root if this is null.
*/
void setParentItem(TreeItem parentItem) {
this.parentItem = parentItem;
}
/**
* This label will be displayed to the right of the bitmap,
* or, if the receiver doesn't have a bitmap to the right of
* the horizontal hierarchy connector line.
*/
public void setText(String newText) {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
Tree parent = getParent();
String oldText = getText();
int redrawX = 0;
int redrawWidth = 0;
if (newText == null) {
error(SWT.ERROR_NULL_ARGUMENT);
}
super.setText(newText);
if (newText.equals(oldText) == false) {
if (parent.getVisibleRedrawY(this) != -1) {
redrawX = getTextXPos();
redrawWidth = parent.getClientArea().width - redrawX;
}
parent.itemChanged(this, redrawX, redrawWidth);
}
}
/**
* Set the y position of the receiver's text to 'yPosition'.
*/
void setTextYPosition(int yPosition) {
textYPosition = yPosition;
}
public void dispose() {
if (!isValidWidget ()) return;
// if the tree is being disposed don't bother collapsing the item since all
// items in the tree will be deleted and redraws will not be processed anyway
Tree parent = getParent();
if (parent.isRemovingAll() == false) {
parent.collapseNoRedraw(this);
}
if (parentItem != null) {
parentItem.removeItem(this);
}
else {
parent.removeItem(this);
}
super.dispose();
}
void doDispose() {
// Notify the parent that the receiver is being removed.
// Reset cached data.
setParentItem(null);
setImageExtent(null);
setItemExtent(null);
setIndex(-1);
setPaintStartX(-1);
setTextYPosition(-1);
super.doDispose();
}
/**
* Returns <code>true</code> if the receiver is checked,
* and false otherwise. When the parent does not have
* the <code>CHECK style, return false.
* <p>
*
* @return the checked state
*
* @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 boolean getChecked() {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
return super.getChecked();
}
public Display getDisplay() {
return super.getDisplay();
}
/**
* Returns <code>true</code> if the receiver is grayed,
* and false otherwise. When the parent does not have
* the <code>CHECK style, return false.
* <p>
*
* @return the grayed state
*
* @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 boolean getGrayed() {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
return super.getGrayed();
}
/**
* Sets the checked state of the receiver.
* <p>
*
* @param checked the new checked state
*
* @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 setChecked(boolean checked) {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
super.setChecked(checked);
}
/**
* Sets the grayed state of the receiver.
* <p>
*
* @param checked the new grayed state
*
* @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 setGrayed (boolean grayed) {
if (!isValidThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if (!isValidWidget ()) error (SWT.ERROR_WIDGET_DISPOSED);
super.setGrayed(grayed);
}
}