| /******************************************************************************* |
| * Copyright (c) 2000, 2008 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.widgets; |
| |
| import org.eclipse.swt.*; |
| import org.eclipse.swt.graphics.*; |
| import org.eclipse.swt.internal.Compatibility; |
| |
| /** |
| * 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> |
| * |
| * @see <a href="http://www.eclipse.org/swt/snippets/#tree">Tree, TreeItem, TreeColumn snippets</a> |
| * @see <a href="http://www.eclipse.org/swt/">Sample code and further information</a> |
| * @noextend This class is not intended to be subclassed by clients. |
| */ |
| public class TreeItem extends Item { |
| Tree parent; |
| TreeItem parentItem; |
| TreeItem[] items = Tree.NO_ITEMS; |
| int availableIndex = -1; /* index in parent's flat list of available (though not necessarily within viewport) items */ |
| int depth = 0; /* cached for performance, does not change after instantiation */ |
| boolean checked, grayed, expanded, cached; |
| |
| String[] texts; |
| int[] textWidths = new int [1]; /* cached string measurements */ |
| int customWidth = -1; /* width specified by Measure callback */ |
| int fontHeight; /* cached item font height */ |
| int[] fontHeights; |
| Image[] images; |
| Color foreground, background; |
| String[] displayTexts; |
| Color[] cellForegrounds, cellBackgrounds; |
| Font font; |
| Font[] cellFonts; |
| |
| static final int INDENT_HIERARCHY = 6; /* the margin between an item's expander and its checkbox or content */ |
| static final int MARGIN_TEXT = 3; /* the left and right margins within the text's space */ |
| |
| /** |
| * Constructs a new instance of this class given its parent |
| * (which must be a <code>Tree</code> or a <code>TreeItem</code>) |
| * and a style value describing its behavior and appearance. |
| * The item is added to the end of the items maintained by its parent. |
| * <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 |
| * @see Widget#checkSubclass |
| * @see Widget#getStyle |
| */ |
| public TreeItem (Tree parent, int style) { |
| this (parent, style, checkNull (parent).items.length); |
| } |
| /** |
| * Constructs a new instance of this class given its parent |
| * (which must be a <code>Tree</code> or a <code>TreeItem</code>), |
| * a style value describing its behavior and appearance, and the index |
| * at which to place it in the items maintained by its parent. |
| * <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 |
| * @param index the zero-relative index to store the receiver in its parent |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</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 |
| * @see Widget#checkSubclass |
| * @see Widget#getStyle |
| */ |
| public TreeItem (Tree parent, int style, int index) { |
| this (parent, style, index, true); |
| } |
| /** |
| * Constructs a new instance of this class given its parent |
| * (which must be a <code>Tree</code> or a <code>TreeItem</code>) |
| * and a style value describing its behavior and appearance. |
| * The item is added to the end of the items maintained by its parent. |
| * <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 parentItem 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 |
| * @see Widget#checkSubclass |
| * @see Widget#getStyle |
| */ |
| public TreeItem (TreeItem parentItem, int style) { |
| this (parentItem, style, checkNull (parentItem).items.length); |
| } |
| /** |
| * Constructs a new instance of this class given its parent |
| * (which must be a <code>Tree</code> or a <code>TreeItem</code>), |
| * a style value describing its behavior and appearance, and the index |
| * at which to place it in the items maintained by its parent. |
| * <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 parentItem a tree control which will be the parent of the new instance (cannot be null) |
| * @param style the style of control to construct |
| * @param index the zero-relative index to store the receiver in its parent |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the parent is null</li> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the parent (inclusive)</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 |
| * @see Widget#checkSubclass |
| * @see Widget#getStyle |
| */ |
| public TreeItem (TreeItem parentItem, int style, int index) { |
| this (parentItem, style, index, true); |
| } |
| TreeItem (TreeItem parentItem, int style, int index, boolean notifyParent) { |
| super (parentItem, style); |
| this.parentItem = parentItem; |
| parent = parentItem.parent; |
| depth = parentItem.depth + 1; |
| int validItemIndex = parentItem.items.length; |
| if (!(0 <= index && index <= validItemIndex)) error (SWT.ERROR_INVALID_RANGE); |
| int columnCount = parent.columns.length; |
| if (columnCount > 0) { |
| displayTexts = new String [columnCount]; |
| if (columnCount > 1) { |
| texts = new String [columnCount]; |
| textWidths = new int [columnCount]; |
| images = new Image [columnCount]; |
| } |
| } |
| if (notifyParent) parentItem.addItem (this, index); |
| } |
| TreeItem (Tree parent, int style, int index, boolean notifyParent) { |
| super (parent, style); |
| int validItemIndex = parent.items.length; |
| if (!(0 <= index && index <= validItemIndex)) error (SWT.ERROR_INVALID_RANGE); |
| this.parent = parent; |
| int columnCount = parent.columns.length; |
| if (columnCount > 0) { |
| displayTexts = new String [columnCount]; |
| if (columnCount > 1) { |
| texts = new String [columnCount]; |
| textWidths = new int [columnCount]; |
| images = new Image [columnCount]; |
| } |
| } |
| if (notifyParent) parent.createItem (this, index); |
| } |
| /* |
| * Updates internal structures in the receiver and its child items to handle the creation of a new column. |
| */ |
| void addColumn (TreeColumn column) { |
| int index = column.getIndex (); |
| int columnCount = parent.columns.length; |
| |
| if (columnCount > 1) { |
| if (columnCount == 2) { |
| texts = new String [2]; |
| } else { |
| String[] newTexts = new String [columnCount]; |
| System.arraycopy (texts, 0, newTexts, 0, index); |
| System.arraycopy (texts, index, newTexts, index + 1, columnCount - index - 1); |
| texts = newTexts; |
| } |
| if (index == 0) { |
| texts [1] = text; |
| text = ""; //$NON-NLS-1$ |
| } |
| |
| if (columnCount == 2) { |
| images = new Image [2]; |
| } else { |
| Image[] newImages = new Image [columnCount]; |
| System.arraycopy (images, 0, newImages, 0, index); |
| System.arraycopy (images, index, newImages, index + 1, columnCount - index - 1); |
| images = newImages; |
| } |
| if (index == 0) { |
| images [1] = image; |
| image = null; |
| } |
| |
| int[] newTextWidths = new int [columnCount]; |
| System.arraycopy (textWidths, 0, newTextWidths, 0, index); |
| System.arraycopy (textWidths, index, newTextWidths, index + 1, columnCount - index - 1); |
| textWidths = newTextWidths; |
| } else { |
| customWidth = -1; /* columnCount == 1 */ |
| } |
| |
| /* |
| * The length of displayTexts always matches the parent's column count, unless this |
| * count is zero, in which case displayTexts is null. |
| */ |
| String[] newDisplayTexts = new String [columnCount]; |
| if (columnCount > 1) { |
| System.arraycopy (displayTexts, 0, newDisplayTexts, 0, index); |
| System.arraycopy (displayTexts, index, newDisplayTexts, index + 1, columnCount - index - 1); |
| } |
| displayTexts = newDisplayTexts; |
| |
| if (cellBackgrounds != null) { |
| Color[] newCellBackgrounds = new Color [columnCount]; |
| System.arraycopy (cellBackgrounds, 0, newCellBackgrounds, 0, index); |
| System.arraycopy (cellBackgrounds, index, newCellBackgrounds, index + 1, columnCount - index - 1); |
| cellBackgrounds = newCellBackgrounds; |
| } |
| if (cellForegrounds != null) { |
| Color[] newCellForegrounds = new Color [columnCount]; |
| System.arraycopy (cellForegrounds, 0, newCellForegrounds, 0, index); |
| System.arraycopy (cellForegrounds, index, newCellForegrounds, index + 1, columnCount - index - 1); |
| cellForegrounds = newCellForegrounds; |
| } |
| if (cellFonts != null) { |
| Font[] newCellFonts = new Font [columnCount]; |
| System.arraycopy (cellFonts, 0, newCellFonts, 0, index); |
| System.arraycopy (cellFonts, index, newCellFonts, index + 1, columnCount - index - 1); |
| cellFonts = newCellFonts; |
| |
| int[] newFontHeights = new int [columnCount]; |
| System.arraycopy (fontHeights, 0, newFontHeights, 0, index); |
| System.arraycopy (fontHeights, index, newFontHeights, index + 1, columnCount - index - 1); |
| fontHeights = newFontHeights; |
| } |
| |
| int orderedIndex = column.getOrderIndex (); |
| if (orderedIndex == 0 && columnCount > 1) { |
| /* |
| * The new second ordered column now has more space available to it than it did while |
| * it was the first ordered column since it no longer has to show hierarchy decorations, |
| * so recompute its displayText. |
| */ |
| TreeColumn[] orderedColumns = parent.getOrderedColumns (); |
| int secondColumnIndex = orderedColumns [1].getIndex (); |
| GC gc = new GC (parent); |
| gc.setFont (getFont (secondColumnIndex, false)); |
| computeDisplayText (secondColumnIndex, gc); |
| gc.dispose (); |
| } |
| |
| /* notify all child items as well */ |
| for (int i = 0; i < items.length; i++) { |
| items[i].addColumn (column); |
| } |
| } |
| /* |
| * Adds a child item to the receiver. |
| */ |
| void addItem (TreeItem item, int index) { |
| TreeItem[] newChildren = new TreeItem [items.length + 1]; |
| System.arraycopy (items, 0, newChildren, 0, index); |
| newChildren [index] = item; |
| System.arraycopy (items, index, newChildren, index + 1, items.length - index); |
| items = newChildren; |
| |
| if (!item.isAvailable ()) { |
| /* receiver will now need an expander box if this is its first child */ |
| if (isInViewport () && items.length == 1) { |
| Rectangle bounds = getExpanderBounds (); |
| parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false); |
| } |
| return; |
| } |
| |
| /* item should be available immediately so update parent */ |
| parent.makeAvailable (item); |
| |
| /* update scrollbars */ |
| Rectangle bounds = item.getBounds (false); |
| int rightX = bounds.x + bounds.width; |
| parent.updateHorizontalBar (rightX, rightX); |
| parent.updateVerticalBar (); |
| /* |
| * If new item is above viewport then adjust topIndex and the vertical scrollbar |
| * so that the current viewport items will not change. |
| */ |
| if (item.availableIndex < parent.topIndex) { |
| parent.topIndex++; |
| ScrollBar vBar = parent.getVerticalBar (); |
| if (vBar != null) vBar.setSelection (parent.topIndex); |
| return; |
| } |
| |
| parent.redrawFromItemDownwards (availableIndex); |
| } |
| static Tree checkNull (Tree tree) { |
| if (tree == null) SWT.error (SWT.ERROR_NULL_ARGUMENT); |
| return tree; |
| } |
| 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); |
| } |
| void clear () { |
| checked = grayed = false; |
| texts = null; |
| textWidths = new int [1]; |
| fontHeight = 0; |
| fontHeights = null; |
| images = null; |
| foreground = background = null; |
| displayTexts = null; |
| cellForegrounds = cellBackgrounds = null; |
| font = null; |
| cellFonts = null; |
| cached = false; |
| text = ""; |
| image = null; |
| |
| int columnCount = parent.columns.length; |
| if (columnCount > 0) { |
| displayTexts = new String [columnCount]; |
| if (columnCount > 1) { |
| texts = new String [columnCount]; |
| textWidths = new int [columnCount]; |
| images = new Image [columnCount]; |
| } |
| } |
| } |
| /** |
| * Clears the item at the given zero-relative index in the receiver. |
| * The text, icon and other attributes of the item are set to the default |
| * value. If the tree was created with the <code>SWT.VIRTUAL</code> style, |
| * these attributes are requested again as needed. |
| * |
| * @param index the index of the item to clear |
| * @param all <code>true</code> if all child items of the indexed item should be |
| * cleared recursively, and <code>false</code> otherwise |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</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 SWT#VIRTUAL |
| * @see SWT#SetData |
| * |
| * @since 3.2 |
| */ |
| public void clear (int index, boolean recursive) { |
| checkWidget (); |
| if (!(0 <= index && index < items.length)) error (SWT.ERROR_INVALID_RANGE); |
| TreeItem item = items [index]; |
| |
| /* if there are no columns then the horizontal scrollbar may need adjusting */ |
| TreeItem[] availableDescendents = null; |
| int oldRightX = 0; |
| if (item.availableIndex != -1 && parent.columns.length == 0) { |
| if (recursive) { |
| availableDescendents = item.computeAvailableDescendents (); |
| for (int i = 0; i < availableDescendents.length; i++) { |
| Rectangle bounds = availableDescendents [i].getBounds (false); |
| oldRightX = Math.max (oldRightX, bounds.x + bounds.width); |
| } |
| } else { |
| Rectangle bounds = item.getBounds (false); |
| oldRightX = bounds.x + bounds.width; |
| } |
| } |
| |
| /* clear the item(s) */ |
| item.clear (); |
| if (recursive) { |
| item.clearAll (true, false); |
| } |
| if (item.availableIndex == -1) return; /* no visual update needed */ |
| |
| /* adjust the horizontal scrollbar if needed */ |
| if (parent.columns.length == 0) { |
| int newRightX = 0; |
| if (recursive) { |
| for (int i = 0; i < availableDescendents.length; i++) { |
| Rectangle bounds = availableDescendents [i].getBounds (false); |
| newRightX = Math.max (newRightX, bounds.x + bounds.width); |
| } |
| } else { |
| Rectangle bounds = item.getBounds (false); |
| newRightX = bounds.x + bounds.width; |
| } |
| parent.updateHorizontalBar (newRightX, newRightX - oldRightX); |
| } |
| |
| /* redraw the item(s) */ |
| if (recursive) { |
| int descendentCount = availableDescendents == null ? |
| item.computeAvailableDescendentCount () : |
| availableDescendents.length; |
| parent.redrawItems (item.availableIndex, item.availableIndex + descendentCount - 1, false); |
| } else { |
| parent.redrawItem (item.availableIndex, false); |
| } |
| } |
| /** |
| * Clears all the items in the receiver. The text, icon and other |
| * attributes of the items are set to their default values. If the |
| * tree was created with the <code>SWT.VIRTUAL</code> style, these |
| * attributes are requested again as needed. |
| * |
| * @param all <code>true</code> if all child items should be cleared |
| * recursively, and <code>false</code> otherwise |
| * |
| * @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 SWT#VIRTUAL |
| * @see SWT#SetData |
| * |
| * @since 3.2 |
| */ |
| public void clearAll (boolean recursive) { |
| clearAll (recursive, true); |
| } |
| void clearAll (boolean recursive, boolean doVisualUpdate) { |
| checkWidget (); |
| if (items.length == 0) return; |
| |
| /* if there are no columns then the horizontal scrollbar may need adjusting */ |
| TreeItem[] availableDescendents = null; |
| int oldRightX = 0; |
| if (doVisualUpdate && availableIndex != -1 && expanded && parent.columns.length == 0) { |
| if (recursive) { |
| availableDescendents = computeAvailableDescendents (); |
| /* |
| * i starts at 1 here because item 0 in availableDescendents |
| * will be the receiver, but this item is not being cleared. |
| */ |
| for (int i = 1; i < availableDescendents.length; i++) { |
| Rectangle bounds = availableDescendents [i].getBounds (false); |
| oldRightX = Math.max (oldRightX, bounds.x + bounds.width); |
| } |
| } else { |
| for (int i = 0; i < items.length; i++) { |
| Rectangle bounds = items [i].getBounds (false); |
| oldRightX = Math.max (oldRightX, bounds.x + bounds.width); |
| } |
| } |
| } |
| |
| /* clear the item(s) */ |
| for (int i = 0; i < items.length; i++) { |
| items [i].clear (); |
| if (recursive) items [i].clearAll (true, false); |
| } |
| |
| if (!doVisualUpdate || availableIndex == -1 || !expanded) return; /* no visual update needed */ |
| |
| /* adjust the horizontal scrollbar if needed */ |
| if (parent.columns.length == 0) { |
| int newRightX = 0; |
| if (recursive) { |
| /* |
| * i starts at 1 here because item 0 in availableDescendents |
| * is the receiver, but this item was not cleared. |
| */ |
| for (int i = 1; i < availableDescendents.length; i++) { |
| Rectangle bounds = availableDescendents [i].getBounds (false); |
| newRightX = Math.max (newRightX, bounds.x + bounds.width); |
| } |
| } else { |
| /* |
| * All cleared direct child items will have the same x and width |
| * values now, so just measure the first one as a sample. |
| */ |
| Rectangle bounds = items [0].getBounds (false); |
| newRightX = bounds.x + bounds.width; |
| } |
| parent.updateHorizontalBar (newRightX, newRightX - oldRightX); |
| } |
| |
| /* redraw the item(s) */ |
| if (recursive) { |
| int startIndex = items [0].availableIndex; |
| TreeItem lastChild = items [items.length - 1]; |
| int endIndex = lastChild.availableIndex + lastChild.computeAvailableDescendentCount () - 1; |
| parent.redrawItems (startIndex, endIndex, false); |
| } else { |
| for (int i = 0; i < items.length; i++) { |
| parent.redrawItem (items [i].availableIndex, false); |
| } |
| } |
| } |
| /* |
| * Returns a collection of all tree items descending from the receiver, including |
| * the receiver. The order of the items in this collection are receiver, child0tree, |
| * child1tree, ..., childNtree. |
| */ |
| TreeItem[] computeAllDescendents () { |
| int childCount = items.length; |
| TreeItem[][] childResults = new TreeItem [childCount][]; |
| int count = 1; /* receiver */ |
| for (int i = 0; i < childCount; i++) { |
| childResults [i] = items [i].computeAllDescendents (); |
| count += childResults [i].length; |
| } |
| TreeItem[] result = new TreeItem [count]; |
| int index = 0; |
| result [index++] = this; |
| for (int i = 0; i < childCount; i++) { |
| System.arraycopy (childResults [i], 0, result, index, childResults [i].length); |
| index += childResults [i].length; |
| } |
| return result; |
| } |
| /* |
| * Returns the number of tree items descending from the receiver, including the |
| * receiver, that are currently available. It is assumed that the receiver is |
| * currently available. |
| */ |
| int computeAvailableDescendentCount () { |
| int result = 1; /* receiver */ |
| if (!expanded) return result; |
| for (int i = 0; i < items.length; i++) { |
| result += items [i].computeAvailableDescendentCount (); |
| } |
| return result; |
| } |
| /* |
| * Returns a collection of the tree items descending from the receiver, including |
| * the receiver, that are currently available. It is assumed that the receiver is |
| * currently available. The order of the items in this collection are receiver, |
| * child0tree, child1tree, ..., childNtree. |
| */ |
| TreeItem[] computeAvailableDescendents () { |
| if (!expanded) return new TreeItem[] {this}; |
| int childCount = items.length; |
| TreeItem[][] childResults = new TreeItem [childCount][]; |
| int count = 1; /* receiver */ |
| for (int i = 0; i < childCount; i++) { |
| childResults [i] = items [i].computeAvailableDescendents (); |
| count += childResults [i].length; |
| } |
| TreeItem[] result = new TreeItem [count]; |
| int index = 0; |
| result [index++] = this; |
| for (int i = 0; i < childCount; i++) { |
| System.arraycopy (childResults [i], 0, result, index, childResults [i].length); |
| index += childResults [i].length; |
| } |
| return result; |
| } |
| void computeDisplayText (int columnIndex, GC gc) { |
| if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */ |
| |
| int columnCount = parent.columns.length; |
| if (columnCount == 0) { |
| String text = getText (0, false); |
| textWidths [columnIndex] = gc.stringExtent (text).x; |
| return; |
| } |
| |
| int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex (); |
| TreeColumn column = parent.columns [columnIndex]; |
| int availableWidth; |
| if (orderedIndex == 0) { |
| /* ordered column 0 is always LEFT and must consider hierarchy decorations */ |
| availableWidth = column.getX () + column.width - getTextX (columnIndex) - 2 * MARGIN_TEXT; |
| } else { |
| /* ordered columns > 0 may not be LEFT so cannot use getTextX (int) */ |
| availableWidth = column.width - 2 * parent.getCellPadding () - 2 * MARGIN_TEXT; |
| if (images [columnIndex] != null) { |
| availableWidth -= images [columnIndex].getBounds ().width; |
| availableWidth -= Tree.MARGIN_IMAGE; |
| } |
| } |
| String text = getText (columnIndex, false); |
| int textWidth = gc.stringExtent (text).x; |
| if (textWidth <= availableWidth) { |
| displayTexts [columnIndex] = text; |
| textWidths [columnIndex] = textWidth; |
| return; |
| } |
| |
| /* Ellipsis will be needed, so subtract their width from the available text width */ |
| int ellipsisWidth = gc.stringExtent (Tree.ELLIPSIS).x; |
| availableWidth -= ellipsisWidth; |
| if (availableWidth <= 0) { |
| displayTexts [columnIndex] = Tree.ELLIPSIS; |
| textWidths [columnIndex] = ellipsisWidth; |
| return; |
| } |
| |
| /* Make initial guess. */ |
| int index = Math.min (availableWidth / gc.getFontMetrics ().getAverageCharWidth (), text.length ()); |
| textWidth = gc.stringExtent (text.substring (0, index)).x; |
| |
| /* Initial guess is correct. */ |
| if (availableWidth == textWidth) { |
| displayTexts [columnIndex] = text.substring (0, index) + Tree.ELLIPSIS; |
| textWidths [columnIndex] = textWidth + ellipsisWidth; |
| return; |
| } |
| |
| /* Initial guess is too high, so reduce until fit is found. */ |
| if (availableWidth < textWidth) { |
| do { |
| index--; |
| if (index < 0) { |
| displayTexts [columnIndex] = Tree.ELLIPSIS; |
| textWidths [columnIndex] = ellipsisWidth; |
| return; |
| } |
| text = text.substring (0, index); |
| textWidth = gc.stringExtent (text).x; |
| } while (availableWidth < textWidth); |
| displayTexts [columnIndex] = text + Tree.ELLIPSIS; |
| textWidths [columnIndex] = textWidth + ellipsisWidth; |
| return; |
| } |
| |
| /* Initial guess is too low, so increase until overrun is found. */ |
| int previousWidth = 0; |
| while (textWidth < availableWidth) { |
| index++; |
| previousWidth = textWidth; |
| textWidth = gc.stringExtent (text.substring (0, index)).x; |
| } |
| displayTexts [columnIndex] = text.substring (0, index - 1) + Tree.ELLIPSIS; |
| textWidths [columnIndex] = previousWidth + ellipsisWidth; |
| } |
| void computeDisplayTexts (GC gc) { |
| if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */ |
| |
| int columnCount = parent.columns.length; |
| if (columnCount == 0) return; |
| |
| for (int i = 0; i < columnCount; i++) { |
| gc.setFont (getFont (i, false)); |
| computeDisplayText (i, gc); |
| } |
| } |
| /* |
| * Computes the cached text widths. |
| */ |
| void computeTextWidths (GC gc) { |
| if ((parent.style & SWT.VIRTUAL) != 0 && !cached) return; /* nothing to do */ |
| |
| int validColumnCount = Math.max (1, parent.columns.length); |
| textWidths = new int [validColumnCount]; |
| for (int i = 0; i < textWidths.length; i++) { |
| String value = getDisplayText (i); |
| if (value != null) { |
| gc.setFont (getFont (i, false)); |
| textWidths [i] = gc.stringExtent (value).x; |
| } |
| } |
| } |
| public void dispose () { |
| if (isDisposed ()) return; |
| int startIndex = -1, endIndex = -1; |
| Tree parent = this.parent; |
| int index = getIndex (); |
| |
| /* determine the indices, if any, that will need to be visually updated */ |
| if (isAvailable ()) { |
| if (isLastChild () && index > 0) { |
| /* vertical connector lines no longer needed for this item */ |
| if (parentItem != null) { |
| startIndex = parentItem.items [index - 1].availableIndex; |
| } else { |
| startIndex = parent.items [index - 1].availableIndex; |
| } |
| } else { |
| startIndex = availableIndex; |
| } |
| endIndex = parent.availableItemsCount - 1; |
| } |
| |
| /* for performance do this upfront for whole descendent chain */ |
| TreeItem focusItem = parent.focusItem; |
| if (focusItem != null && focusItem.hasAncestor (this)) { |
| parent.setFocusItem (this, false); |
| parent.reassignFocus (); |
| focusItem = parent.focusItem; |
| if (focusItem != null) { |
| parent.redrawItem (focusItem.availableIndex, true); |
| } |
| } |
| if (parentItem != null) parentItem.removeItem (this, index); |
| dispose (true); |
| if (startIndex != -1) { |
| parent.redrawItems (startIndex, endIndex, false); |
| } |
| } |
| void dispose (boolean notifyParent) { |
| if (isDisposed ()) return; |
| for (int i = 0; i < items.length; i++) { |
| items [i].dispose (notifyParent); |
| } |
| if (notifyParent) parent.destroyItem (this); |
| super.dispose (); /* super is intentional here */ |
| background = foreground = null; |
| cellBackgrounds = cellForegrounds = null; |
| font = null; |
| cellFonts = null; |
| images = null; |
| texts = displayTexts = null; |
| textWidths = fontHeights = null; |
| parent = null; |
| parentItem = null; |
| items = null; |
| } |
| /* |
| * Ensure that all ancestors of the receiver are expanded |
| */ |
| void expandAncestors () { |
| if (parentItem != null) parentItem.expandAncestors (); |
| setExpanded (true); |
| Event newEvent = new Event (); |
| newEvent.item = this; |
| parent.inExpand = true; |
| parent.sendEvent (SWT.Expand, newEvent); |
| parent.inExpand = false; |
| if (isDisposed ()) return; |
| if (items.length == 0) { |
| expanded = false; |
| } |
| } |
| /** |
| * Returns the receiver's background color. |
| * |
| * @return the background color |
| * |
| * @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> |
| * |
| * @since 2.0 |
| * |
| */ |
| public Color getBackground () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| if (background != null) return background; |
| return parent.getBackground (); |
| } |
| /** |
| * Returns the background color at the given column index in the receiver. |
| * |
| * @param index the column index |
| * @return the background color |
| * |
| * @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> |
| * |
| * @since 3.1 |
| */ |
| public Color getBackground (int columnIndex) { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getBackground (); |
| if (cellBackgrounds == null || cellBackgrounds [columnIndex] == null) return getBackground (); |
| return cellBackgrounds [columnIndex]; |
| } |
| /** |
| * 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 () { |
| checkWidget (); |
| return getBounds (true); |
| } |
| Rectangle getBounds (boolean checkData) { |
| if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| if (!isAvailable ()) return new Rectangle (0, 0, 0, 0); |
| TreeColumn[] orderedColumns = parent.getOrderedColumns (); |
| int orderedCol0Index = orderedColumns.length == 0 ? 0 : orderedColumns [0].getIndex (); |
| int x = getTextX (orderedCol0Index); |
| int width = textWidths [orderedCol0Index] + 2 * MARGIN_TEXT; |
| if (orderedColumns.length > 0) { |
| TreeColumn column = orderedColumns [0]; |
| int right = column.getX () + column.width; |
| if (x + width > right) { |
| width = Math.max (0, right - x); |
| } |
| } |
| return new Rectangle (x, parent.getItemY (this), width, parent.itemHeight); |
| } |
| /** |
| * Returns a rectangle describing the receiver's size and location |
| * relative to its parent at a column in the tree. |
| * |
| * @param index the index that specifies the column |
| * @return the receiver's bounding column 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> |
| * |
| * @since 3.1 |
| */ |
| public Rectangle getBounds (int columnIndex) { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| if (!isAvailable ()) return new Rectangle (0, 0, 0, 0); |
| TreeColumn[] columns = parent.columns; |
| int columnCount = columns.length; |
| int validColumnCount = Math.max (1, columnCount); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) { |
| return new Rectangle (0, 0, 0, 0); |
| } |
| /* |
| * If there are no columns then this is the bounds of the receiver's content. |
| */ |
| if (columnCount == 0) { |
| return new Rectangle ( |
| getContentX (0), |
| parent.getItemY (this), |
| getContentWidth (0), |
| parent.itemHeight - 1); |
| } |
| |
| TreeColumn column = columns [columnIndex]; |
| if (column.getOrderIndex () == 0) { |
| /* |
| * For ordered column 0 this is bounds from the beginning of the content to the |
| * end of the column. |
| */ |
| int x = getContentX (columnIndex); |
| int offset = x - column.getX (); |
| int width = Math.max (0, column.width - offset - 1); /* max is for columns with small widths */ |
| return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1); |
| } |
| /* |
| * For ordered columns > 0 this is the bounds of the tree cell. |
| */ |
| return new Rectangle (column.getX (), parent.getItemY (this) + 1, column.width, parent.itemHeight - 1); |
| } |
| /* |
| * Returns the full bounds of a cell in a tree, regardless of its content. |
| */ |
| Rectangle getCellBounds (int columnIndex) { |
| int y = parent.getItemY (this); |
| if (parent.columns.length == 0) { |
| int width; |
| if (customWidth != -1) { |
| width = getContentX (0) + customWidth + parent.horizontalOffset; |
| } else { |
| int textPaintWidth = textWidths [0] + 2 * MARGIN_TEXT; |
| width = getTextX (0) + textPaintWidth + parent.horizontalOffset; |
| } |
| return new Rectangle (-parent.horizontalOffset, y, width, parent.itemHeight); |
| } |
| TreeColumn column = parent.columns [columnIndex]; |
| return new Rectangle (column.getX (), y, column.width, parent.itemHeight); |
| } |
| /* |
| * Returns the bounds of the receiver's checkbox, or null if the parent's style does not |
| * include SWT.CHECK. |
| */ |
| Rectangle getCheckboxBounds () { |
| if ((parent.getStyle () & SWT.CHECK) == 0) return null; |
| int itemHeight = parent.itemHeight; |
| Rectangle result = parent.checkboxBounds; |
| Point[] hLinePoints = getHconnectorEndpoints (); |
| result.x = hLinePoints [1].x; |
| result.y = parent.getItemY (this) + (itemHeight - result.height) / 2; |
| return result; |
| } |
| /** |
| * 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 () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| return checked; |
| } |
| int getContentWidth (int columnIndex) { |
| int width = textWidths [columnIndex] + 2 * MARGIN_TEXT; |
| int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex (); |
| if (orderedIndex == 0) { |
| width += parent.orderedCol0imageWidth; |
| if (parent.orderedCol0imageWidth > 0) width += Tree.MARGIN_IMAGE; |
| } else { |
| Image image = getImage (columnIndex, false); |
| if (image != null) { |
| width += image.getBounds ().width + Tree.MARGIN_IMAGE; |
| } |
| } |
| return width; |
| } |
| /* |
| * Returns the x value where the receiver's content (ie.- its image or text) begins |
| * for the specified column. For ordered columns > 0 this is dependent upon column |
| * alignment, and for ordered column 0 this is dependent upon the receiver's depth in |
| * the tree item hierarchy and the presence/absence of a checkbox. |
| */ |
| int getContentX (int columnIndex) { |
| int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex (); |
| if (orderedIndex > 0) { |
| TreeColumn column = parent.columns [columnIndex]; |
| int contentX = column.getX () + parent.getCellPadding (); |
| if ((column.style & SWT.LEFT) != 0) return contentX; |
| |
| /* column is not left-aligned */ |
| int contentWidth = getContentWidth (columnIndex); |
| if ((column.style & SWT.RIGHT) != 0) { |
| int padding = parent.getCellPadding (); |
| contentX = Math.max (contentX, column.getX () + column.width - padding - contentWidth); |
| } else { /* SWT.CENTER */ |
| contentX = Math.max (contentX, column.getX () + (column.width - contentWidth) / 2); |
| } |
| return contentX; |
| } |
| |
| /* ordered column 0 (always left-aligned) */ |
| if ((parent.style & SWT.CHECK) != 0) { |
| Rectangle checkBounds = getCheckboxBounds (); |
| return checkBounds.x + checkBounds.width + Tree.MARGIN_IMAGE; |
| } |
| |
| int contentX = parent.getCellPadding () - parent.horizontalOffset; |
| if (parentItem != null) { |
| int expanderWidth = parent.expanderBounds.width + INDENT_HIERARCHY; |
| contentX += expanderWidth * depth; |
| } |
| contentX += parent.expanderBounds.width; |
| return contentX + Tree.MARGIN_IMAGE + INDENT_HIERARCHY; |
| } |
| String getDisplayText (int columnIndex) { |
| if (parent.columns.length == 0) return getText (0, false); |
| String result = displayTexts [columnIndex]; |
| return result != null ? result : ""; //$NON-NLS-1$ |
| } |
| /** |
| * Returns <code>true</code> if the receiver is expanded, |
| * and false otherwise. |
| * <p> |
| * |
| * @return the 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 boolean getExpanded () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| return expanded; |
| } |
| /* |
| * Returns the bounds of the receiver's expander box, regardless of whether the |
| * receiver currently has children or not. |
| */ |
| Rectangle getExpanderBounds () { |
| int itemHeight = parent.itemHeight; |
| int x = parent.getCellPadding () - parent.horizontalOffset; |
| int y = parent.getItemY (this); |
| if (parentItem != null) { |
| int expanderWidth = parent.expanderBounds.width + INDENT_HIERARCHY; |
| x += expanderWidth * depth; |
| } |
| return new Rectangle ( |
| x, y + (itemHeight - parent.expanderBounds.height) / 2, |
| parent.expanderBounds.width, parent.expanderBounds.height); |
| } |
| /* |
| * Returns the bounds that should be used for drawing a focus rectangle on the receiver |
| */ |
| Rectangle getFocusBounds () { |
| TreeColumn[] columns = parent.columns; |
| int orderedCol0index = columns.length == 0 ? 0 : parent.getOrderedColumns ()[0].getIndex (); |
| int x = getTextX (orderedCol0index); |
| |
| int width; |
| if (columns.length > 0) { |
| /* ensure that the focus x does not start beyond the right bound of ordered column 0 */ |
| int rightX = columns [orderedCol0index].getX () + columns [orderedCol0index].width; |
| x = Math.min (x, rightX - 1); |
| |
| TreeColumn column; |
| if ((parent.style & SWT.FULL_SELECTION) != 0) { |
| int[] columnOrder = parent.getColumnOrder (); |
| column = columns [columnOrder [columnOrder.length - 1]]; /* last ordered column */ |
| } else { |
| column = columns [orderedCol0index]; |
| } |
| width = column.getX () + column.width - x - 1; |
| } else { /* no columns */ |
| if (customWidth != -1) { |
| width = customWidth; |
| } else { |
| width = textWidths [0] + 2 * MARGIN_TEXT; |
| } |
| } |
| |
| return new Rectangle ( |
| x, |
| parent.getItemY (this) + (parent.linesVisible ? 1 : 0), |
| width, |
| parent.itemHeight - (parent.linesVisible ? 1 : 0)); |
| } |
| /** |
| * Returns the font that the receiver will use to paint textual information for this item. |
| * |
| * @return the receiver's font |
| * |
| * @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> |
| * |
| * @since 3.0 |
| */ |
| public Font getFont () { |
| checkWidget (); |
| return getFont (true); |
| } |
| Font getFont (boolean checkData) { |
| if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| if (font != null) return font; |
| return parent.getFont (); |
| } |
| /** |
| * Returns the font that the receiver will use to paint textual information |
| * for the specified cell in this item. |
| * |
| * @param index the column index |
| * @return the receiver's font |
| * |
| * @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> |
| * |
| * @since 3.1 |
| */ |
| public Font getFont (int columnIndex) { |
| checkWidget (); |
| return getFont (columnIndex, true); |
| } |
| Font getFont (int columnIndex, boolean checkData) { |
| if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getFont (checkData); |
| if (cellFonts == null || cellFonts [columnIndex] == null) return getFont (checkData); |
| return cellFonts [columnIndex]; |
| } |
| int getFontHeight () { |
| if (fontHeight != 0) return fontHeight; |
| return parent.fontHeight; |
| } |
| int getFontHeight (int columnIndex) { |
| if (fontHeights == null || fontHeights [columnIndex] == 0) return getFontHeight (); |
| return fontHeights [columnIndex]; |
| } |
| /** |
| * Returns the foreground color that the receiver will use to draw. |
| * |
| * @return the receiver's foreground color |
| * |
| * @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> |
| * |
| * @since 2.0 |
| * |
| */ |
| public Color getForeground () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| if (foreground != null) return foreground; |
| return parent.getForeground (); |
| } |
| /** |
| * |
| * Returns the foreground color at the given column index in the receiver. |
| * |
| * @param index the column index |
| * @return the foreground color |
| * |
| * @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> |
| * |
| * @since 3.1 |
| */ |
| public Color getForeground (int columnIndex) { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return getForeground (); |
| if (cellForegrounds == null || cellForegrounds [columnIndex] == null) return getForeground (); |
| return cellForegrounds [columnIndex]; |
| } |
| /** |
| * 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 of the checkbox |
| * |
| * @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 () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| return grayed; |
| } |
| /* |
| * Answers the start and end points of the horizontal connector line that is |
| * drawn between an item's expander box and its checkbox or content. |
| */ |
| Point[] getHconnectorEndpoints () { |
| Rectangle expanderBounds = getExpanderBounds (); |
| int x, width; |
| if (items.length == 0) { /* no child items, so no expander box */ |
| x = expanderBounds.x + Compatibility.ceil (expanderBounds.width, 2); |
| width = Compatibility.floor (expanderBounds.width, 2) + INDENT_HIERARCHY; |
| } else { /* has child items */ |
| x = expanderBounds.x + expanderBounds.width; |
| width = INDENT_HIERARCHY; |
| } |
| int y = expanderBounds.y + expanderBounds.height / 2; |
| return new Point[] { |
| new Point (x, y), |
| new Point (x + width, y) |
| }; |
| } |
| /* |
| * Returns the bounds representing the clickable region that should select the receiver. |
| */ |
| Rectangle getHitBounds () { |
| int[] columnOrder = parent.getColumnOrder (); |
| int orderedCol0index = columnOrder.length == 0 ? 0 : parent.columns [columnOrder [0]].getIndex (); |
| int contentX = getContentX (orderedCol0index); |
| int width = 0; |
| TreeColumn[] columns = parent.columns; |
| if (columns.length == 0) { |
| width = getContentWidth (0); |
| } else { |
| /* |
| * If there are columns then this spans from the beginning of the receiver's column 0 |
| * image or text to the end of either column 0 or the last column (FULL_SELECTION). |
| */ |
| TreeColumn column; |
| if ((parent.style & SWT.FULL_SELECTION) != 0) { |
| column = columns [columnOrder [columnOrder.length - 1]]; /* last column */ |
| } else { |
| column = columns [orderedCol0index]; |
| } |
| width = column.getX () + column.width - contentX; |
| } |
| return new Rectangle (contentX, parent.getItemY (this), width, parent.itemHeight); |
| } |
| public Image getImage () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| return super.getImage (); |
| } |
| /** |
| * Returns the image stored at the given column index in the receiver, |
| * or null if the image has not been set or if the column does not exist. |
| * |
| * @param index the column index |
| * @return the image stored at the given column index in the receiver |
| * |
| * @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> |
| * |
| * @since 3.1 |
| */ |
| public Image getImage (int columnIndex) { |
| checkWidget (); |
| return getImage (columnIndex, true); |
| } |
| Image getImage (int columnIndex, boolean checkData) { |
| if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return null; |
| if (columnIndex == 0) return super.getImage (); /* super is intentional here */ |
| return images [columnIndex]; |
| } |
| /** |
| * Returns a rectangle describing the size and location |
| * relative to its parent of an image at a column in the |
| * tree. |
| * |
| * @param index the index that specifies the column |
| * @return the receiver's bounding image 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> |
| * |
| * @since 3.1 |
| */ |
| public Rectangle getImageBounds (int columnIndex) { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return new Rectangle (0,0,0,0); |
| |
| int padding = parent.getCellPadding (); |
| int startX = getContentX (columnIndex); |
| int itemHeight = parent.itemHeight; |
| int imageSpaceY = itemHeight - 2 * padding; |
| int y = parent.getItemY (this); |
| int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex (); |
| Image image = getImage (columnIndex, false); |
| int drawWidth = 0; |
| if (orderedIndex == 0) { |
| /* for ordered column 0 all images have the same width */ |
| drawWidth = parent.orderedCol0imageWidth; |
| } else { |
| if (image != null) drawWidth = image.getBounds ().width; |
| } |
| return new Rectangle (startX, y + padding, drawWidth, imageSpaceY); |
| } |
| int getIndex () { |
| TreeItem[] items; |
| if (parentItem != null) { |
| items = parentItem.items; |
| } else { |
| items = parent.items; |
| } |
| for (int i = 0; i < items.length; i++) { |
| if (items [i] == this) return i; |
| } |
| return -1; |
| } |
| /** |
| * Returns the item at the given, zero-relative index in the |
| * receiver. Throws an exception if the index is out of range. |
| * |
| * @param index the index of the item to return |
| * @return the item at the given index |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_RANGE - if the index is not between 0 and the number of elements in the list minus 1 (inclusive)</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> |
| * |
| * @since 3.1 |
| */ |
| public TreeItem getItem (int index) { |
| checkWidget (); |
| if (index < 0) error (SWT.ERROR_INVALID_RANGE); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| if (index >= items.length) error (SWT.ERROR_INVALID_RANGE); |
| return items [index]; |
| } |
| /** |
| * 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 () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| return items.length; |
| } |
| String getNameText () { |
| if ((parent.style & SWT.VIRTUAL) != 0) { |
| if (!cached) return "*virtual*"; //$NON-NLS-1$ |
| } |
| return super.getNameText (); |
| } |
| /** |
| * Returns a (possibly empty) 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 () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| TreeItem[] result = new TreeItem [items.length]; |
| System.arraycopy (items, 0, result, 0, items.length); |
| return result; |
| } |
| /** |
| * 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 () { |
| checkWidget (); |
| return parent; |
| } |
| /** |
| * 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 () { |
| checkWidget (); |
| return parentItem; |
| } |
| /* |
| * Returns the receiver's ideal width for the specified columnIndex. |
| */ |
| int getPreferredWidth (int columnIndex) { |
| int width = 0; |
| GC gc = new GC (parent); |
| gc.setFont (getFont (columnIndex, false)); |
| width += gc.stringExtent (getText (columnIndex, false)).x + 2 * MARGIN_TEXT; |
| int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex (); |
| if (orderedIndex == 0) { |
| if (parent.orderedCol0imageWidth > 0) { |
| width += parent.orderedCol0imageWidth; |
| width += Tree.MARGIN_IMAGE; |
| } |
| } else { |
| Image image = getImage (columnIndex, false); |
| if (image != null) { |
| width += image.getBounds ().width; |
| width += Tree.MARGIN_IMAGE; |
| } |
| } |
| |
| if (parent.hooks (SWT.MeasureItem)) { |
| Event event = new Event (); |
| event.item = this; |
| event.gc = gc; |
| event.index = columnIndex; |
| event.x = getContentX (columnIndex); |
| event.y = parent.getItemY (this); |
| event.width = width; |
| event.height = parent.itemHeight; |
| parent.sendEvent (SWT.MeasureItem, event); |
| if (parent.itemHeight != event.height) { |
| parent.customHeightSet = true; |
| boolean update = parent.setItemHeight (event.height + 2 * parent.getCellPadding ()); |
| if (update) parent.redraw (); |
| } |
| width = event.width; |
| } |
| gc.dispose (); |
| |
| if (orderedIndex == 0) { |
| return getContentX (columnIndex) + parent.horizontalOffset + width + parent.getCellPadding (); /* right side cell pad */ |
| } |
| |
| return width + 2 * parent.getCellPadding (); |
| } |
| public String getText () { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| return super.getText (); |
| } |
| /** |
| * Returns the text stored at the given column index in the receiver, |
| * or empty string if the text has not been set. |
| * |
| * @param index the column index |
| * @return the text stored at the given column index in the receiver |
| * |
| * @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> |
| * |
| * @since 3.1 |
| */ |
| public String getText (int columnIndex) { |
| checkWidget (); |
| return getText (columnIndex, true); |
| } |
| String getText (int columnIndex, boolean checkData) { |
| if (checkData && !parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return ""; //$NON-NLS-1$ |
| if (columnIndex == 0) return super.getText (); /* super is intentional here */ |
| if (texts [columnIndex] == null) return ""; //$NON-NLS-1$ |
| return texts [columnIndex]; |
| } |
| /** |
| * Returns a rectangle describing the size and location |
| * relative to its parent of the text at a column in the |
| * tree. |
| * |
| * @param index the index that specifies the column |
| * @return the receiver's bounding text 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> |
| * |
| * @since 3.3 |
| */ |
| public Rectangle getTextBounds (int columnIndex) { |
| checkWidget (); |
| if (!parent.checkData (this, true)) error (SWT.ERROR_WIDGET_DISPOSED); |
| if (!isAvailable ()) return new Rectangle (0, 0, 0, 0); |
| TreeColumn[] columns = parent.columns; |
| int columnCount = columns.length; |
| int validColumnCount = Math.max (1, columnCount); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) { |
| return new Rectangle (0, 0, 0, 0); |
| } |
| /* |
| * If there are no columns then this is the bounds of the receiver's content. |
| */ |
| if (columnCount == 0) { |
| int x = getTextX (0) + MARGIN_TEXT; |
| int width = Math.max (0, getContentX(0) + getContentWidth (0) - x); |
| return new Rectangle ( |
| x, |
| parent.getItemY (this), |
| width, |
| parent.itemHeight - 1); |
| } |
| |
| TreeColumn column = columns [columnIndex]; |
| if (column.getOrderIndex () == 0) { |
| /* |
| * For ordered column 0 this is bounds from the beginning of the content to the |
| * end of the column. |
| */ |
| int x = getTextX (columnIndex) + MARGIN_TEXT; |
| int offset = x - column.getX (); |
| int width = Math.max (0, column.width - offset - 1); /* max is for columns with small widths */ |
| return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1); |
| } |
| /* |
| * For ordered columns > 0 this is the bounds of the tree cell. |
| */ |
| int x = getTextX (columnIndex) + MARGIN_TEXT; |
| int offset = x - column.getX (); |
| int width = Math.max (0, column.width - offset - MARGIN_TEXT); |
| return new Rectangle (x, parent.getItemY (this) + 1, width, parent.itemHeight - 1); |
| } |
| /* |
| * Returns the x value where the receiver's text begins. |
| */ |
| int getTextX (int columnIndex) { |
| int orderedIndex = parent.columns.length == 0 ? 0 : parent.columns [columnIndex].getOrderIndex (); |
| int textX = getContentX (columnIndex); |
| if (orderedIndex == 0) { |
| textX += parent.orderedCol0imageWidth; |
| if (parent.orderedCol0imageWidth > 0) textX += Tree.MARGIN_IMAGE; |
| } else { |
| Image image = getImage (columnIndex, false); |
| if (image != null) { |
| textX += image.getBounds ().width + Tree.MARGIN_IMAGE; |
| } |
| } |
| return textX; |
| } |
| /* |
| * Returns true if the receiver descends from (or is identical to) the item. |
| */ |
| boolean hasAncestor (TreeItem item) { |
| if (this == item) return true; |
| if (parentItem == null) return false; |
| return parentItem.hasAncestor (item); |
| } |
| /** |
| * Searches the receiver's list starting at the first item |
| * (index 0) until an item is found that is equal to the |
| * argument, and returns the index of that item. If no item |
| * is found, returns -1. |
| * |
| * @param item the search item |
| * @return the index of the item |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the item is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if the item 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> |
| * |
| * @since 3.1 |
| */ |
| public int indexOf (TreeItem item) { |
| checkWidget (); |
| if (item == null) error (SWT.ERROR_NULL_ARGUMENT); |
| if (item.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); |
| if (item.parentItem != this) return -1; |
| return item.getIndex (); |
| } |
| /* |
| * Returns true if the receiver is currently available (though not necessary in the viewport). |
| */ |
| boolean isAvailable () { |
| if (parentItem == null) return true; /* root items are always available */ |
| if (!parentItem.expanded) return false; |
| return parentItem.isAvailable (); |
| } |
| /* |
| * Answers a boolean indicating whether the receiver's y is within the current |
| * viewport of the parent. |
| */ |
| boolean isInViewport () { |
| if (availableIndex == -1) return false; |
| int topIndex = parent.topIndex; |
| if (availableIndex < topIndex) return false; |
| int visibleCount = parent.clientArea.height / parent.itemHeight; |
| return availableIndex <= topIndex + visibleCount; |
| } |
| /* |
| * Returns true if the receiver is the last child of its parent item, or of its parent |
| * if the receiver is a root item, and false otherwise. |
| */ |
| boolean isLastChild () { |
| if (parentItem != null) { |
| return getIndex () == parentItem.items.length - 1; |
| } |
| return getIndex () == parent.items.length - 1; |
| } |
| boolean isSelected () { |
| return parent.getSelectionIndex (this) != -1; |
| } |
| /* |
| * The backgroundOnly argument indicates whether the item should only |
| * worry about painting its background color and selection. |
| * |
| * Returns a boolean indicating whether to abort drawing focus on the item. |
| * If the receiver is not the current focus item then this value is irrelevant. |
| */ |
| boolean paint (GC gc, TreeColumn column, boolean backgroundOnly) { |
| if (!parent.checkData (this, true)) return false; |
| int columnIndex = 0, orderedIndex = 0, x = 0; |
| if (column != null) { |
| columnIndex = column.getIndex (); |
| orderedIndex = column.getOrderIndex (); |
| x = column.getX (); |
| } |
| |
| /* |
| * Capture GC attributes that will need to be restored later in the paint |
| * process to ensure that the item paints as intended without being affected |
| * by GC changes made in MeasureItem/EraseItem/PaintItem callbacks. |
| */ |
| int oldAlpha = gc.getAlpha (); |
| boolean oldAdvanced = gc.getAdvanced (); |
| int oldAntialias = gc.getAntialias (); |
| Pattern oldBackgroundPattern = gc.getBackgroundPattern (); |
| Pattern oldForegroundPattern = gc.getForegroundPattern (); |
| int oldInterpolation = gc.getInterpolation (); |
| int[] oldLineDash = gc.getLineDash (); |
| int oldLineWidth = gc.getLineWidth (); |
| int oldTextAntialias = gc.getTextAntialias (); |
| |
| if (parent.hooks (SWT.MeasureItem)) { |
| int contentWidth = getContentWidth (columnIndex); |
| int contentX = getContentX (columnIndex); |
| gc.setFont (getFont (columnIndex, false)); |
| Event event = new Event (); |
| event.item = this; |
| event.gc = gc; |
| event.index = columnIndex; |
| event.x = contentX; |
| event.y = parent.getItemY (this); |
| event.width = contentWidth; |
| event.height = parent.itemHeight; |
| parent.sendEvent (SWT.MeasureItem, event); |
| event.gc = null; |
| if (gc.isDisposed ()) return false; |
| gc.setAlpha (oldAlpha); |
| gc.setAntialias (oldAntialias); |
| gc.setBackgroundPattern (oldBackgroundPattern); |
| gc.setForegroundPattern (oldForegroundPattern); |
| gc.setInterpolation (oldInterpolation); |
| gc.setLineDash (oldLineDash); |
| gc.setLineWidth (oldLineWidth); |
| gc.setTextAntialias (oldTextAntialias); |
| gc.setAdvanced (oldAdvanced); |
| if (isDisposed ()) return false; |
| if (parent.itemHeight != event.height) { |
| parent.customHeightSet = true; |
| boolean update = parent.setItemHeight (event.height + 2 * parent.getCellPadding ()); |
| if (update) parent.redraw (); |
| } |
| if (parent.columns.length == 0) { |
| int change = event.width - (customWidth != -1 ? customWidth : contentWidth); |
| if (event.width != contentWidth || customWidth != -1) customWidth = event.width; |
| if (change != 0) { /* scrollbar may be affected since no columns */ |
| parent.updateHorizontalBar (contentX + event.width, change); |
| // TODO what if clip is too small now? |
| } |
| } |
| } |
| |
| /* if this cell is completely to the right of the client area then there's no need to paint it */ |
| Rectangle clientArea = parent.clientArea; |
| if (clientArea.x + clientArea.width < x) return false; |
| |
| Rectangle cellBounds = getCellBounds (columnIndex); |
| if (parent.linesVisible) { |
| cellBounds.y++; |
| cellBounds.height--; |
| } |
| int cellRightX = 0; |
| if (column != null) { |
| cellRightX = column.getX () + column.width; |
| } else { |
| cellRightX = cellBounds.x + cellBounds.width; |
| } |
| |
| /* restrict the clipping region to the cell */ |
| gc.setClipping (x, cellBounds.y, clientArea.width - x, cellBounds.height); |
| |
| int y = parent.getItemY (this); |
| int itemHeight = parent.itemHeight; |
| |
| /* draw the parent background color/image of this cell */ |
| if (column == null) { |
| parent.drawBackground (gc, 0, y, clientArea.width, itemHeight); |
| } else { |
| int fillWidth = cellBounds.width; |
| if (parent.linesVisible) fillWidth--; |
| parent.drawBackground (gc, cellBounds.x, cellBounds.y, fillWidth, cellBounds.height); |
| } |
| |
| boolean isSelected = isSelected (); |
| boolean isFocusItem = parent.focusItem == this && parent.isFocusControl (); |
| boolean drawBackground = true; |
| boolean drawForeground = true; |
| boolean drawSelection = isSelected; |
| boolean drawFocus = isFocusItem; |
| if (parent.hooks (SWT.EraseItem)) { |
| drawBackground = background != null || (cellBackgrounds != null && cellBackgrounds [columnIndex] != null); |
| gc.setFont (getFont (columnIndex, false)); |
| if (isSelected && (columnIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) { |
| gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT)); |
| gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION)); |
| } else { |
| gc.setForeground (getForeground (columnIndex)); |
| gc.setBackground (getBackground (columnIndex)); |
| } |
| Event event = new Event (); |
| event.item = this; |
| event.gc = gc; |
| event.index = columnIndex; |
| event.doit = true; |
| event.detail = SWT.FOREGROUND; |
| if (drawBackground) event.detail |= SWT.BACKGROUND; |
| if (isSelected) event.detail |= SWT.SELECTED; |
| if (isFocusItem) event.detail |= SWT.FOCUSED; |
| event.x = cellBounds.x; |
| event.y = cellBounds.y; |
| event.width = cellBounds.width; |
| event.height = cellBounds.height; |
| gc.setClipping (cellBounds); |
| parent.sendEvent (SWT.EraseItem, event); |
| event.gc = null; |
| if (gc.isDisposed ()) return false; |
| gc.setAlpha (oldAlpha); |
| gc.setAntialias (oldAntialias); |
| gc.setBackgroundPattern (oldBackgroundPattern); |
| gc.setClipping (cellBounds); |
| gc.setForegroundPattern (oldForegroundPattern); |
| gc.setInterpolation (oldInterpolation); |
| gc.setLineDash (oldLineDash); |
| gc.setLineWidth (oldLineWidth); |
| gc.setTextAntialias (oldTextAntialias); |
| gc.setAdvanced (oldAdvanced); |
| if (isDisposed ()) return false; |
| if (!event.doit) { |
| drawBackground = drawForeground = drawSelection = drawFocus = false; |
| } else { |
| drawBackground = drawBackground && (event.detail & SWT.BACKGROUND) != 0; |
| drawForeground = (event.detail & SWT.FOREGROUND) != 0; |
| drawSelection = isSelected && (event.detail & SWT.SELECTED) != 0; |
| drawFocus = isFocusItem && (event.detail & SWT.FOCUSED) != 0; |
| } |
| } |
| |
| /* draw the cell's set background if appropriate */ |
| if (drawBackground) { |
| gc.setBackground (getBackground (columnIndex)); |
| if (columnIndex == 0 && (column == null || column.getOrderIndex () == 0)) { |
| Rectangle focusBounds = getFocusBounds (); |
| int fillWidth = 0; |
| if (column == null) { |
| fillWidth = focusBounds.width; |
| } else { |
| fillWidth = column.width - focusBounds.x; |
| if (parent.linesVisible) fillWidth--; |
| } |
| gc.fillRectangle (focusBounds.x, focusBounds.y, fillWidth, focusBounds.height); |
| } else { |
| int fillWidth = cellBounds.width; |
| gc.fillRectangle (cellBounds.x, cellBounds.y, fillWidth, cellBounds.height); |
| } |
| } |
| |
| /* draw the selection bar if the receiver is selected */ |
| if (drawSelection && isSelected && (orderedIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) { |
| gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION)); |
| if (orderedIndex == 0) { |
| Rectangle focusBounds = getFocusBounds (); |
| int fillWidth = focusBounds.width; |
| if (parent.columns.length < 2 || (parent.style & SWT.FULL_SELECTION) == 0) { |
| fillWidth -= 2; /* space for right bound of focus rect */ |
| } |
| if (fillWidth > 0) { |
| gc.fillRectangle (focusBounds.x + 1, focusBounds.y + 1, fillWidth, focusBounds.height - 2); |
| } |
| } else { |
| int fillWidth = column.width; |
| int[] columnOrder = parent.getColumnOrder (); |
| if (columnIndex == columnOrder [columnOrder.length - 1]) { |
| fillWidth -= 2; /* space for right bound of focus rect */ |
| } |
| if (fillWidth > 0) { |
| gc.fillRectangle ( |
| column.getX (), |
| cellBounds.y + 1, |
| fillWidth, |
| cellBounds.height - 2); |
| } |
| } |
| } |
| |
| if (backgroundOnly) return false; |
| |
| /* Draw column 0 decorations */ |
| if (orderedIndex == 0) { |
| gc.setClipping (cellBounds); |
| |
| /* Draw hierarchy connector lines */ |
| Rectangle expanderBounds = getExpanderBounds (); |
| Color oldForeground = gc.getForeground (); |
| gc.setForeground (parent.getConnectorColor ()); |
| |
| /* Draw vertical line above expander */ |
| int lineX = expanderBounds.x + expanderBounds.width / 2; |
| int y2 = expanderBounds.y; |
| if (items.length == 0) { |
| y2 += expanderBounds.height / 2; |
| } |
| /* Do not draw this line iff this is the very first item in the tree */ |
| if (parentItem != null || getIndex () != 0) { |
| gc.drawLine (lineX, y, lineX, y2); |
| } |
| |
| /* Draw vertical line below expander if the receiver has lower siblings */ |
| if (!isLastChild ()) { |
| if (items.length != 0) y2 += expanderBounds.height; |
| gc.drawLine (lineX, y2, lineX, y + itemHeight); |
| } |
| |
| /* Draw horizontal line to right of expander */ |
| Point[] endpoints = getHconnectorEndpoints (); |
| gc.drawLine (endpoints [0].x, endpoints [0].y, endpoints [1].x - Tree.MARGIN_IMAGE, endpoints [1].y); |
| |
| /* |
| * Draw hierarchy lines that are needed by other items that are shown below |
| * this item but whose parents are shown above (ie.- lines to the left of |
| * this item's connector line). |
| */ |
| TreeItem item = parentItem; |
| while (item != null) { |
| if (!item.isLastChild ()) { |
| Rectangle itemExpanderBounds = item.getExpanderBounds (); |
| lineX = itemExpanderBounds.x + itemExpanderBounds.width / 2; |
| gc.drawLine (lineX, y, lineX, y + itemHeight); |
| } |
| item = item.parentItem; |
| } |
| |
| gc.setForeground (oldForeground); |
| |
| /* Draw expand/collapse image if receiver has children */ |
| if (items.length > 0) { |
| Image image = expanded ? parent.getExpandedImage () : parent.getCollapsedImage (); |
| gc.drawImage (image, expanderBounds.x, expanderBounds.y); |
| } |
| |
| /* Draw checkbox if parent Tree has style SWT.CHECK */ |
| if ((parent.style & SWT.CHECK) != 0) { |
| Image baseImage = grayed ? parent.getGrayUncheckedImage () : parent.getUncheckedImage (); |
| Rectangle checkboxBounds = getCheckboxBounds (); |
| gc.drawImage (baseImage, checkboxBounds.x, checkboxBounds.y); |
| /* Draw checkmark if item is checked */ |
| if (checked) { |
| Image checkmarkImage = parent.getCheckmarkImage (); |
| Rectangle checkmarkBounds = checkmarkImage.getBounds (); |
| int xInset = (checkboxBounds.width - checkmarkBounds.width) / 2; |
| int yInset = (checkboxBounds.height - checkmarkBounds.height) / 2; |
| gc.drawImage (checkmarkImage, checkboxBounds.x + xInset, checkboxBounds.y + yInset); |
| } |
| } |
| } |
| |
| if (drawForeground) { |
| Image image = getImage (columnIndex, false); |
| String text = getDisplayText (columnIndex); |
| Rectangle imageArea = getImageBounds (columnIndex); |
| int startX = imageArea.x; |
| |
| /* while painting the cell's content restrict the clipping region */ |
| int padding = parent.getCellPadding (); |
| gc.setClipping ( |
| startX, |
| cellBounds.y + padding - (parent.linesVisible ? 1 : 0), |
| cellRightX - startX - padding, |
| cellBounds.height - 2 * (padding - (parent.linesVisible ? 1 : 0))); |
| |
| /* draw the image */ |
| if (image != null) { |
| Rectangle imageBounds = image.getBounds (); |
| gc.drawImage ( |
| image, |
| 0, 0, /* source x, y */ |
| imageBounds.width, imageBounds.height, /* source width, height */ |
| imageArea.x, imageArea.y, /* dest x, y */ |
| imageArea.width, imageArea.height); /* dest width, height */ |
| } |
| |
| /* draw the text */ |
| if (text.length () > 0) { |
| gc.setFont (getFont (columnIndex, false)); |
| int fontHeight = getFontHeight (columnIndex); |
| if (drawSelection && (orderedIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) { |
| gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT)); |
| } else { |
| if (!isSelected || drawSelection) { |
| gc.setForeground (getForeground (columnIndex)); |
| } |
| } |
| x = getTextX (columnIndex) + MARGIN_TEXT; |
| gc.drawString (text, x, y + (itemHeight - fontHeight) / 2, true); |
| } |
| } |
| |
| if (parent.hooks (SWT.PaintItem)) { |
| int contentWidth = getContentWidth (columnIndex); |
| int contentX = getContentX (columnIndex); |
| gc.setFont (getFont (columnIndex, false)); |
| if (isSelected && (columnIndex == 0 || (parent.style & SWT.FULL_SELECTION) != 0)) { |
| gc.setForeground (display.getSystemColor (SWT.COLOR_LIST_SELECTION_TEXT)); |
| gc.setBackground (display.getSystemColor (SWT.COLOR_LIST_SELECTION)); |
| } else { |
| gc.setForeground (getForeground (columnIndex)); |
| gc.setBackground (getBackground (columnIndex)); |
| } |
| Event event = new Event (); |
| event.item = this; |
| event.gc = gc; |
| event.index = columnIndex; |
| if (isSelected) event.detail |= SWT.SELECTED; |
| if (drawFocus) event.detail |= SWT.FOCUSED; |
| event.x = contentX; |
| event.y = cellBounds.y; |
| event.width = contentWidth; |
| event.height = cellBounds.height; |
| gc.setClipping (cellBounds); |
| parent.sendEvent (SWT.PaintItem, event); |
| event.gc = null; |
| if (gc.isDisposed ()) return false; |
| gc.setAlpha (oldAlpha); |
| gc.setAntialias (oldAntialias); |
| gc.setBackgroundPattern (oldBackgroundPattern); |
| gc.setClipping (cellBounds); |
| gc.setForegroundPattern (oldForegroundPattern); |
| gc.setInterpolation (oldInterpolation); |
| gc.setLineDash (oldLineDash); |
| gc.setLineWidth (oldLineWidth); |
| gc.setTextAntialias (oldTextAntialias); |
| gc.setAdvanced (oldAdvanced); |
| drawFocus = isFocusItem && (event.detail & SWT.FOCUSED) != 0; |
| } |
| |
| return isFocusItem && !drawFocus; |
| } |
| /* |
| * Redraw part of the receiver. If either EraseItem or PaintItem is hooked then |
| * only full cells should be damaged, so adjust accordingly. If neither of these |
| * events are hooked then the exact bounds given for damaging can be used. |
| */ |
| void redraw (int x, int y, int width, int height, int columnIndex) { |
| if (!parent.hooks (SWT.EraseItem) && !parent.hooks (SWT.PaintItem)) { |
| parent.redraw (x, y, width, height, false); |
| return; |
| } |
| Rectangle cellBounds = getCellBounds (columnIndex); |
| parent.redraw (cellBounds.x, cellBounds.y, cellBounds.width, cellBounds.height, false); |
| } |
| void redrawItem () { |
| if (!isAvailable ()) return; |
| parent.redraw (0, parent.getItemY (this), parent.clientArea.width, parent.itemHeight, false); |
| } |
| /* |
| * Updates internal structures in the receiver and its child items to handle the removal of a column. |
| */ |
| void removeColumn (TreeColumn column, int index, int orderedIndex) { |
| int columnCount = parent.columns.length; |
| |
| if (columnCount == 0) { |
| /* reverts to normal tree when last column disposed */ |
| cellBackgrounds = cellForegrounds = null; |
| displayTexts = null; |
| cellFonts = null; |
| fontHeights = null; |
| GC gc = new GC (parent); |
| computeTextWidths (gc); |
| gc.dispose (); |
| /* notify all child items as well */ |
| for (int i = 0; i < items.length; i++) { |
| items [i].removeColumn (column, index, orderedIndex); |
| } |
| return; |
| } |
| |
| String[] newTexts = new String [columnCount]; |
| System.arraycopy (texts, 0, newTexts, 0, index); |
| System.arraycopy (texts, index + 1, newTexts, index, columnCount - index); |
| texts = newTexts; |
| |
| Image[] newImages = new Image [columnCount]; |
| System.arraycopy (images, 0, newImages, 0, index); |
| System.arraycopy (images, index + 1, newImages, index, columnCount - index); |
| images = newImages; |
| |
| int[] newTextWidths = new int [columnCount]; |
| System.arraycopy (textWidths, 0, newTextWidths, 0, index); |
| System.arraycopy (textWidths, index + 1, newTextWidths, index, columnCount - index); |
| textWidths = newTextWidths; |
| |
| String[] newDisplayTexts = new String [columnCount]; |
| System.arraycopy (displayTexts, 0, newDisplayTexts, 0, index); |
| System.arraycopy (displayTexts, index + 1, newDisplayTexts, index, columnCount - index); |
| displayTexts = newDisplayTexts; |
| |
| if (cellBackgrounds != null) { |
| Color[] newCellBackgrounds = new Color [columnCount]; |
| System.arraycopy (cellBackgrounds, 0, newCellBackgrounds, 0, index); |
| System.arraycopy (cellBackgrounds, index + 1, newCellBackgrounds, index, columnCount - index); |
| cellBackgrounds = newCellBackgrounds; |
| } |
| if (cellForegrounds != null) { |
| Color[] newCellForegrounds = new Color [columnCount]; |
| System.arraycopy (cellForegrounds, 0, newCellForegrounds, 0, index); |
| System.arraycopy (cellForegrounds, index + 1, newCellForegrounds, index, columnCount - index); |
| cellForegrounds = newCellForegrounds; |
| } |
| if (cellFonts != null) { |
| Font[] newCellFonts = new Font [columnCount]; |
| System.arraycopy (cellFonts, 0, newCellFonts, 0, index); |
| System.arraycopy (cellFonts, index + 1, newCellFonts, index, columnCount - index); |
| cellFonts = newCellFonts; |
| |
| int[] newFontHeights = new int [columnCount]; |
| System.arraycopy (fontHeights, 0, newFontHeights, 0, index); |
| System.arraycopy (fontHeights, index + 1, newFontHeights, index, columnCount - index); |
| fontHeights = newFontHeights; |
| } |
| |
| if (index == 0) { |
| text = texts [0] != null ? texts [0] : ""; //$NON-NLS-1$ |
| texts [0] = null; |
| image = images [0]; |
| images [0] = null; |
| } |
| |
| if (orderedIndex == 0) { |
| /* |
| * The new first ordered column will not have as much width available to it as it did when |
| * it was the second ordered column since it now has to show hierarchy decorations as well, |
| * so recompute its displayText. |
| */ |
| int firstColumnIndex = parent.getOrderedColumns () [0].getIndex (); |
| GC gc = new GC (parent); |
| gc.setFont (getFont (firstColumnIndex, false)); |
| computeDisplayText (firstColumnIndex, gc); |
| gc.dispose (); |
| } |
| if (columnCount < 2) { |
| texts = null; |
| images = null; |
| } |
| |
| /* notify all child items as well */ |
| for (int i = 0; i < items.length; i++) { |
| items [i].removeColumn (column, index, orderedIndex); |
| } |
| } |
| /** |
| * Removes all of the items from the receiver. |
| * <p> |
| * @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> |
| * |
| * @since 3.1 |
| */ |
| public void removeAll () { |
| checkWidget (); |
| if (items.length == 0) return; |
| |
| int lastAvailableIndex = parent.availableItemsCount - 1; |
| /* for performance do this upfront for whole descendent chain */ |
| TreeItem focusItem = parent.focusItem; |
| if (focusItem != null && focusItem.hasAncestor (this)) { |
| parent.setFocusItem (this, false); |
| } |
| while (items.length > 0) { |
| items [0].dispose (true); |
| removeItem (items [0], 0); |
| } |
| items = Tree.NO_ITEMS; |
| expanded = false; |
| if (isAvailable ()) { |
| parent.redrawItems (availableIndex, lastAvailableIndex, false); |
| } |
| } |
| /* |
| * Removes a child item from the receiver. |
| */ |
| void removeItem (TreeItem item, int index) { |
| if (isDisposed ()) return; |
| TreeItem[] newItems = new TreeItem [items.length - 1]; |
| System.arraycopy (items, 0, newItems, 0, index); |
| System.arraycopy (items, index + 1, newItems, index, newItems.length - index); |
| items = newItems; |
| if (items.length == 0) { |
| items = Tree.NO_ITEMS; |
| /* condition below handles creation of item within Expand callback */ |
| if (!parent.inExpand) { |
| expanded = false; |
| if (isInViewport ()) { |
| Rectangle bounds = getExpanderBounds (); /* expander box no longer needed */ |
| parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false); |
| } |
| } |
| return; |
| } |
| } |
| /** |
| * Sets the receiver's background color to the color specified |
| * by the argument, or to the default system color for the item |
| * if the argument is null. |
| * |
| * @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> |
| * |
| * @since 2.0 |
| * |
| */ |
| public void setBackground (Color color) { |
| checkWidget (); |
| if (color != null && color.isDisposed ()) { |
| SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| } |
| Color oldColor = background; |
| if (oldColor == color) return; |
| background = color; |
| if (oldColor != null && oldColor.equals (color)) return; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| redrawItem (); |
| } |
| /** |
| * Sets the background color at the given column index in the receiver |
| * to the color specified by the argument, or to the default system color for the item |
| * if the argument is null. |
| * |
| * @param index the column index |
| * @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> |
| * |
| * @since 3.1 |
| * |
| */ |
| public void setBackground (int columnIndex, Color color) { |
| checkWidget (); |
| if (color != null && color.isDisposed ()) { |
| SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| } |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return; |
| if (cellBackgrounds == null) { |
| if (color == null) return; |
| cellBackgrounds = new Color [validColumnCount]; |
| } |
| Color oldColor = cellBackgrounds [columnIndex]; |
| if (oldColor == color) return; |
| cellBackgrounds [columnIndex] = color; |
| if (oldColor != null && oldColor.equals (color)) return; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| if (isInViewport ()) { |
| Rectangle bounds = getCellBounds (columnIndex); |
| parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false); |
| } |
| } |
| /** |
| * 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 value) { |
| checkWidget (); |
| if ((parent.getStyle () & SWT.CHECK) == 0) return; |
| if (checked == value) return; |
| checked = value; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| if (isInViewport ()) { |
| if (parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) { |
| redrawItem (); |
| } else { |
| Rectangle bounds = getCheckboxBounds (); |
| parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false); |
| } |
| } |
| } |
| /** |
| * 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 value) { |
| checkWidget (); |
| if (expanded == value) return; |
| if (items.length == 0) return; |
| if (parent.inExpand) return; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| if (value) { |
| expanded = value; |
| if (availableIndex == -1) return; |
| |
| TreeItem[] availableDescendents = computeAvailableDescendents (); |
| int descendentsCount = availableDescendents.length; |
| if (availableIndex != parent.availableItemsCount - 1) { |
| /* the receiver is not the last available item */ |
| Rectangle clientArea = parent.clientArea; |
| int y = parent.getItemY (this) + parent.itemHeight; |
| if (0 < y && y < clientArea.height) { |
| if (parent.drawCount <= 0) { |
| parent.update (); |
| GC gc = new GC (parent); |
| gc.copyArea ( |
| 0, y, |
| clientArea.width, clientArea.height - y, |
| 0, y + ((descendentsCount - 1) * parent.itemHeight)); |
| gc.dispose (); |
| } |
| } |
| } |
| |
| parent.makeDescendentsAvailable (this, availableDescendents); |
| |
| /* update scrollbars */ |
| int rightX = 0; |
| for (int i = 1; i < availableDescendents.length; i++) { |
| Rectangle bounds = availableDescendents [i].getBounds (false); |
| rightX = Math.max (rightX, bounds.x + bounds.width); |
| } |
| parent.updateHorizontalBar (rightX, rightX); |
| parent.updateVerticalBar (); |
| /* |
| * If new item is above viewport then adjust topIndex and the vertical scrollbar |
| * so that the current viewport items will not change. |
| */ |
| if (availableIndex < parent.topIndex) { |
| parent.topIndex += descendentsCount - 1; |
| ScrollBar vBar = parent.getVerticalBar (); |
| if (vBar != null) vBar.setSelection (parent.topIndex); |
| return; |
| } |
| |
| int redrawStart = availableIndex + 1; |
| int redrawEnd = redrawStart + descendentsCount - 2; |
| parent.redrawItems (redrawStart, redrawEnd, false); |
| } else { |
| TreeItem[] descendents = computeAvailableDescendents (); |
| expanded = value; |
| if (availableIndex == -1) return; |
| Rectangle clientArea = parent.clientArea; |
| |
| int y = parent.getItemY (this) + parent.itemHeight; |
| int startY = y + (descendents.length - 1) * parent.itemHeight; |
| if (y < clientArea.height && 0 < startY) { /* determine whether any visual update is actually needed */ |
| if (parent.drawCount <= 0) { |
| parent.update (); |
| GC gc = new GC (parent); |
| gc.copyArea (0, startY, clientArea.width, clientArea.height - startY, 0, y); |
| gc.dispose (); |
| int redrawY = y + Math.max (0, clientArea.height - startY); |
| parent.redraw (0, redrawY, clientArea.width, clientArea.height - redrawY, false); |
| } |
| } |
| |
| parent.makeDescendentsUnavailable (this, descendents); |
| |
| /* |
| * If all collapsed items are above the viewport then adjust topIndex and |
| * the vertical scrollbar so that the current viewport items will not change. |
| */ |
| int bottomIndex = availableIndex + descendents.length - 1; |
| if (bottomIndex < parent.topIndex) { |
| parent.topIndex = parent.topIndex - descendents.length + 1; |
| ScrollBar vBar = parent.getVerticalBar (); |
| if (vBar != null) vBar.setSelection (parent.topIndex); |
| } |
| |
| parent.updateHorizontalBar (); |
| parent.updateVerticalBar (); |
| |
| /* move focus (and selection if SWT.SINGLE) to item if a descendent had focus */ |
| TreeItem focusItem = parent.focusItem; |
| if (focusItem != null && focusItem != this && focusItem.hasAncestor (this)) { |
| parent.setFocusItem (this, false); |
| if ((parent.style & SWT.SINGLE) != 0) { |
| parent.selectItem (this, false); |
| } |
| /* Fire an event since the selection is being changed automatically */ |
| Event newEvent = new Event (); |
| newEvent.item = this; |
| parent.sendEvent (SWT.Selection, newEvent); |
| if (isDisposed ()) return; |
| parent.showItem (this); |
| parent.redrawItem (availableIndex, true); |
| } |
| } |
| /* redraw the receiver's expander box */ |
| if (isInViewport ()) { |
| Rectangle bounds = getExpanderBounds (); |
| parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false); |
| } |
| } |
| /** |
| * Sets the font that the receiver will use to paint textual information |
| * for this item to the font specified by the argument, or to the default font |
| * for that kind of control if the argument is null. |
| * |
| * @param font the new font (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> |
| * |
| * @since 3.0 |
| */ |
| public void setFont (Font font) { |
| checkWidget (); |
| if (font != null && font.isDisposed ()) { |
| SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| } |
| Font oldFont = this.font; |
| if (oldFont == font) return; |
| this.font = font; |
| if (oldFont != null && oldFont.equals (font)) return; |
| |
| Rectangle bounds = getBounds (false); |
| int oldRightX = bounds.x + bounds.width; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| |
| /* recompute cached values for string measurements */ |
| GC gc = new GC (parent); |
| gc.setFont (getFont (false)); |
| fontHeight = gc.getFontMetrics ().getHeight (); |
| computeDisplayTexts (gc); |
| computeTextWidths (gc); |
| gc.dispose (); |
| |
| /* horizontal bar could be affected if tree has no columns */ |
| if (parent.columns.length == 0) { |
| bounds = getBounds (false); |
| int newRightX = bounds.x + bounds.width; |
| parent.updateHorizontalBar (newRightX, newRightX - oldRightX); |
| } |
| redrawItem (); |
| } |
| /** |
| * Sets the font that the receiver will use to paint textual information |
| * for the specified cell in this item to the font specified by the |
| * argument, or to the default font for that kind of control if the |
| * argument is null. |
| * |
| * @param index the column index |
| * @param font the new font (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> |
| * |
| * @since 3.1 |
| */ |
| public void setFont (int columnIndex, Font font) { |
| checkWidget (); |
| if (font != null && font.isDisposed ()) { |
| SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| } |
| |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return; |
| if (cellFonts == null) { |
| if (font == null) return; |
| cellFonts = new Font [validColumnCount]; |
| } |
| Font oldFont = cellFonts [columnIndex]; |
| if (oldFont == font) return; |
| cellFonts [columnIndex] = font; |
| if (oldFont != null && oldFont.equals (font)) return; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| |
| /* recompute cached values for string measurements */ |
| GC gc = new GC (parent); |
| gc.setFont (getFont (columnIndex, false)); |
| if (fontHeights == null) fontHeights = new int [validColumnCount]; |
| fontHeights [columnIndex] = gc.getFontMetrics ().getHeight (); |
| computeDisplayText (columnIndex, gc); |
| gc.dispose (); |
| |
| if (isInViewport ()) { |
| Rectangle bounds = getCellBounds (columnIndex); |
| parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false); |
| } |
| } |
| /** |
| * Sets the receiver's foreground color to the color specified |
| * by the argument, or to the default system color for the item |
| * if the argument is null. |
| * |
| * @param color the new color (or null) |
| * |
| * @since 2.0 |
| * |
| * @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> |
| * |
| * @since 2.0 |
| * |
| */ |
| public void setForeground (Color color) { |
| checkWidget (); |
| if (color != null && color.isDisposed ()) { |
| SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| } |
| Color oldColor = foreground; |
| if (oldColor == color) return; |
| foreground = color; |
| if (oldColor != null && oldColor.equals (color)) return; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| redrawItem (); |
| } |
| /** |
| * Sets the foreground color at the given column index in the receiver |
| * to the color specified by the argument, or to the default system color for the item |
| * if the argument is null. |
| * |
| * @param index the column index |
| * @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> |
| * |
| * @since 3.1 |
| * |
| */ |
| public void setForeground (int columnIndex, Color value) { |
| checkWidget (); |
| if (value != null && value.isDisposed ()) { |
| SWT.error (SWT.ERROR_INVALID_ARGUMENT); |
| } |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return; |
| if (cellForegrounds == null) { |
| if (value == null) return; |
| cellForegrounds = new Color [validColumnCount]; |
| } |
| Color oldColor = cellForegrounds [columnIndex]; |
| if (oldColor == value) return; |
| cellForegrounds [columnIndex] = value; |
| if (oldColor != null && oldColor.equals (value)) return; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| if (isInViewport ()) { |
| redraw ( |
| getTextX (columnIndex), |
| parent.getItemY (this), |
| textWidths [columnIndex] + 2 * MARGIN_TEXT, |
| parent.itemHeight, |
| columnIndex); |
| } |
| } |
| /** |
| * Sets the grayed state of the checkbox for this item. This state change |
| * only applies if the Tree was created with the SWT.CHECK style. |
| * |
| * @param grayed the new grayed state of the checkbox |
| * |
| * @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 value) { |
| checkWidget (); |
| if ((parent.getStyle () & SWT.CHECK) == 0) return; |
| if (grayed == value) return; |
| grayed = value; |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| if (isInViewport ()) { |
| Rectangle bounds = getCheckboxBounds (); |
| parent.redraw (bounds.x, bounds.y, bounds.width, bounds.height, false); |
| } |
| } |
| public void setImage (Image value) { |
| checkWidget (); |
| setImage (0, value); |
| } |
| /** |
| * Sets the image for multiple columns in the tree. |
| * |
| * @param images the array of new images |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the array of images is null</li> |
| * <li>ERROR_INVALID_ARGUMENT - if one of the images 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> |
| * |
| * @since 3.1 |
| */ |
| public void setImage (Image[] value) { |
| checkWidget (); |
| if (value == null) error (SWT.ERROR_NULL_ARGUMENT); |
| |
| // TODO make a smarter implementation of this |
| for (int i = 0; i < value.length; i++) { |
| if (value [i] != null) setImage (i, value [i]); |
| } |
| } |
| /** |
| * Sets the receiver's image at a column. |
| * |
| * @param index the column index |
| * @param image the new image |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_INVALID_ARGUMENT - if the image 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> |
| * |
| * @since 3.1 |
| */ |
| public void setImage (int columnIndex, Image value) { |
| checkWidget (); |
| if (value != null && value.isDisposed ()) error (SWT.ERROR_INVALID_ARGUMENT); |
| |
| TreeColumn[] columns = parent.columns; |
| int validColumnCount = Math.max (1, columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return; |
| Image image = getImage (columnIndex, false); |
| if (value == image) return; |
| if (value != null && value.equals (image)) return; |
| if (columnIndex == 0) { |
| super.setImage (value); |
| } else { |
| images [columnIndex] = value; |
| } |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| |
| /* |
| * An image width change may affect the space available for the item text, so |
| * recompute the displayText if there are columns. |
| */ |
| if (columns.length > 0) { |
| GC gc = new GC (parent); |
| gc.setFont (getFont (columnIndex, false)); |
| computeDisplayText (columnIndex, gc); |
| gc.dispose (); |
| } |
| |
| if (value == null) { |
| redrawItem (); // TODO why the whole item? |
| return; |
| } |
| |
| if (columns.length == 0) { |
| if (parent.imageHeight == 0) { |
| /* this is the first image being put into the parent Tree */ |
| Rectangle bounds = value.getBounds (); |
| parent.orderedCol0imageWidth = bounds.width; |
| parent.setImageHeight (bounds.height); |
| parent.redrawItems (0, parent.availableItemsCount - 1, false); |
| } else { |
| redrawItem (); |
| } |
| return; |
| } |
| |
| /* there are 1+ columns */ |
| TreeColumn column = columns [columnIndex]; |
| int orderedIndex = column.getOrderIndex (); |
| Rectangle bounds = value.getBounds (); |
| if (column.itemImageWidth == 0) column.itemImageWidth = bounds.width; |
| |
| if (parent.imageHeight == 0) { |
| /* this is the first image being put into the parent Tree */ |
| int oldItemHeight = parent.itemHeight; |
| parent.setImageHeight (bounds.height); |
| |
| if (orderedIndex == 0) { /* the first ordered column */ |
| parent.orderedCol0imageWidth = bounds.width; |
| /* |
| * All column 0 cells will now have less room available for their texts, |
| * so all items must now recompute their column 0 displayTexts. |
| */ |
| TreeItem[] rootItems = parent.items; |
| GC gc = new GC (parent); |
| for (int i = 0; i < rootItems.length; i++) { |
| rootItems [i].updateColumnWidth (column, gc); |
| } |
| gc.dispose (); |
| if (oldItemHeight != parent.itemHeight) { |
| /* the item height grew as a result of the new image height, so redraw everything */ |
| parent.redraw (); |
| } else { |
| /* redraw the column since all items should now have image space */ |
| parent.redraw (column.getX (), 0, column.width, parent.clientArea.height, false); |
| } |
| } else { /* not the first ordered column */ |
| if (oldItemHeight != parent.itemHeight) { |
| /* the item height grew as a result of the new image height, so redraw everything */ |
| parent.redraw (); |
| } else { |
| redrawItem (); |
| } |
| } |
| return; |
| } |
| |
| if (orderedIndex == 0 && parent.orderedCol0imageWidth == 0) { |
| /* this is the first image being put into the current ordered column 0 */ |
| parent.orderedCol0imageWidth = bounds.width; |
| /* |
| * All column 0 cells will now have less room available for their texts, |
| * so all items must now recompute their column 0 displayTexts. |
| */ |
| TreeItem[] rootItems = parent.items; |
| GC gc = new GC (parent); |
| for (int i = 0; i < rootItems.length; i++) { |
| rootItems [i].updateColumnWidth (column, gc); |
| } |
| gc.dispose (); |
| parent.redraw (column.getX (), 0, column.width, parent.clientArea.height, false); |
| return; |
| } |
| |
| redrawItem (); // TODO why the whole item? |
| } |
| /** |
| * Sets the number of child items contained in the receiver. |
| * |
| * @param count 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> |
| * |
| * @since 3.2 |
| */ |
| public void setItemCount (int count) { |
| checkWidget (); |
| count = Math.max (0, count); |
| if (count == items.length) return; |
| int redrawStart, redrawEnd; |
| |
| /* if the new item count is less than the current count then remove all excess items from the end */ |
| if (count < items.length) { |
| redrawStart = count > 0 ? items [count - 1].availableIndex : availableIndex; |
| redrawEnd = parent.availableItemsCount - 1; |
| for (int i = count; i < items.length; i++) { |
| items [i].dispose (true); |
| } |
| if (count == 0) { |
| items = Tree.NO_ITEMS; |
| } else { |
| TreeItem[] newItems = new TreeItem [count]; |
| System.arraycopy (items, 0, newItems, 0, count); |
| items = newItems; |
| } |
| if (count == 0) expanded = false; |
| } else { |
| int oldAvailableDescendentCount = computeAvailableDescendentCount (); |
| int grow = count - items.length; |
| redrawStart = items.length == 0 ? availableIndex : items [items.length - 1].availableIndex; |
| redrawEnd = expanded && isAvailable () ? parent.availableItemsCount + grow - 1: redrawStart; |
| TreeItem[] newItems = new TreeItem [count]; |
| System.arraycopy (items, 0, newItems, 0, items.length); |
| items = newItems; |
| for (int i = items.length - grow; i < count; i++) { |
| items [i] = new TreeItem (this, SWT.NONE, i, false); |
| } |
| |
| if (expanded && availableIndex != -1) { |
| /* expand the availableItems array if necessary */ |
| if (parent.availableItems.length < parent.availableItemsCount + grow) { |
| TreeItem[] newAvailableItems = new TreeItem [parent.availableItemsCount + grow]; |
| System.arraycopy (parent.availableItems, 0, newAvailableItems, 0, parent.availableItemsCount); |
| parent.availableItems = newAvailableItems; |
| } |
| TreeItem[] availableItems = parent.availableItems; |
| /* shift items right to create space for the new available items */ |
| int dest = availableIndex + oldAvailableDescendentCount + grow; |
| System.arraycopy ( |
| availableItems, |
| availableIndex + oldAvailableDescendentCount, |
| availableItems, |
| dest, |
| availableItems.length - dest); |
| parent.availableItemsCount += grow; |
| /* copy new items in */ |
| System.arraycopy ( |
| items, |
| items.length - grow, |
| availableItems, |
| availableIndex + oldAvailableDescendentCount, |
| grow); |
| /* update availableIndex for all affected items */ |
| for (int i = availableIndex + oldAvailableDescendentCount; i < parent.availableItemsCount; i++) { |
| availableItems [i].availableIndex = i; |
| } |
| } |
| } |
| |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| if (availableIndex != -1) { |
| if (expanded) parent.updateVerticalBar (); |
| parent.redrawItems (redrawStart, redrawEnd, false); |
| } |
| } |
| public void setText (String value) { |
| checkWidget (); |
| Rectangle bounds = getBounds (false); |
| int oldRightX = bounds.x + bounds.width; |
| setText (0, value); |
| /* horizontal bar could be affected if tree has no columns */ |
| if (parent.columns.length == 0) { |
| bounds = getBounds (false); |
| int newRightX = bounds.x + bounds.width; |
| parent.updateHorizontalBar (newRightX, newRightX - oldRightX); |
| } |
| } |
| /** |
| * Sets the text for multiple columns in the tree. |
| * |
| * @param strings the array of new strings |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the text 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> |
| * |
| * @since 3.1 |
| */ |
| public void setText (String[] value) { |
| checkWidget (); |
| if (value == null) error (SWT.ERROR_NULL_ARGUMENT); |
| Rectangle bounds = getBounds (false); |
| int oldRightX = bounds.x + bounds.width; |
| // TODO make a smarter implementation of this |
| for (int i = 0; i < value.length; i++) { |
| if (value [i] != null) setText (i, value [i]); |
| } |
| /* horizontal bar could be affected if tree has no columns */ |
| if (parent.columns.length == 0) { |
| bounds = getBounds (false); |
| int newRightX = bounds.x + bounds.width; |
| parent.updateHorizontalBar (newRightX, newRightX - oldRightX); |
| } |
| } |
| /** |
| * Sets the receiver's text at a column |
| * |
| * @param index the column index |
| * @param string the new text |
| * |
| * @exception IllegalArgumentException <ul> |
| * <li>ERROR_NULL_ARGUMENT - if the text 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> |
| * |
| * @since 3.1 |
| */ |
| public void setText (int columnIndex, String value) { |
| checkWidget (); |
| if (value == null) error (SWT.ERROR_NULL_ARGUMENT); |
| int validColumnCount = Math.max (1, parent.columns.length); |
| if (!(0 <= columnIndex && columnIndex < validColumnCount)) return; |
| if (value.equals (getText (columnIndex, false))) return; |
| if (columnIndex == 0) { |
| super.setText (value); |
| } else { |
| texts [columnIndex] = value; |
| } |
| if ((parent.style & SWT.VIRTUAL) != 0) cached = true; |
| |
| int oldWidth = textWidths [columnIndex]; |
| GC gc = new GC (parent); |
| gc.setFont (getFont (columnIndex, false)); |
| computeDisplayText (columnIndex, gc); |
| gc.dispose (); |
| if (availableIndex == -1) return; |
| if (parent.columns.length == 0) { |
| Rectangle bounds = getBounds (false); |
| int rightX = bounds.x + bounds.width; |
| parent.updateHorizontalBar (rightX, textWidths [columnIndex] - oldWidth); |
| } |
| if (isInViewport ()) { |
| redraw ( |
| getTextX (columnIndex), |
| parent.getItemY (this), |
| Math.max (oldWidth, textWidths [columnIndex]) + 2 * MARGIN_TEXT, |
| parent.itemHeight, |
| columnIndex); |
| } |
| } |
| /* |
| * Perform any internal changes necessary to reflect a changed column width. |
| */ |
| void updateColumnWidth (TreeColumn column, GC gc) { |
| int columnIndex = column.getIndex (); |
| gc.setFont (getFont (columnIndex, false)); |
| String oldDisplayText = displayTexts [columnIndex]; |
| computeDisplayText (columnIndex, gc); |
| |
| /* the cell must be damaged if there is custom drawing being done or if the alignment is not LEFT */ |
| if (isInViewport ()) { |
| boolean columnIsLeft = (column.style & SWT.LEFT) != 0; |
| if (!columnIsLeft || parent.hooks (SWT.EraseItem) || parent.hooks (SWT.PaintItem)) { |
| Rectangle cellBounds = getCellBounds (columnIndex); |
| parent.redraw (cellBounds.x, cellBounds.y, cellBounds.width, cellBounds.height, false); |
| } else { |
| /* if the display text has changed then the cell text must be damaged in order to repaint */ |
| if (oldDisplayText == null || !oldDisplayText.equals (displayTexts [columnIndex])) { |
| Rectangle cellBounds = getCellBounds (columnIndex); |
| int textX = getTextX (columnIndex); |
| parent.redraw (textX, cellBounds.y, cellBounds.x + cellBounds.width - textX, cellBounds.height, false); |
| } |
| } |
| } |
| |
| for (int i = 0; i < items.length; i++) { |
| items [i].updateColumnWidth (column, gc); |
| } |
| } |
| |
| /* |
| * The parent's font has changed, so if this font was being used by the receiver then |
| * recompute its cached text sizes using the gc argument. Pass this notification on to |
| * all child items as well. |
| */ |
| void updateFont (GC gc) { |
| if (font == null) { /* receiver is using the Tree's font */ |
| computeDisplayTexts (gc); |
| computeTextWidths (gc); |
| } |
| /* pass notification on to all children */ |
| for (int i = 0; i < items.length; i++) { |
| items [i].updateFont (gc); |
| } |
| } |
| } |