2551 lines
96 KiB
Java
2551 lines
96 KiB
Java
|
|
/*
|
||
|
|
* Copyright (c) 2004,2009 Declarative Engineering LLC.
|
||
|
|
* All rights reserved. This program and the accompanying materials
|
||
|
|
* are made available under the terms of the Declarative Engineering LLC
|
||
|
|
* verson 1 which accompanies this distribution, and is available at
|
||
|
|
* http://declarativeengineering.com/legal/DE_Developer_License_v1.txt
|
||
|
|
*/
|
||
|
|
package com.foundation.tcv.swt.client;
|
||
|
|
|
||
|
|
import org.eclipse.swt.SWT;
|
||
|
|
import org.eclipse.swt.custom.TableEditor;
|
||
|
|
import org.eclipse.swt.custom.TreeEditor;
|
||
|
|
import org.eclipse.swt.events.ControlEvent;
|
||
|
|
import org.eclipse.swt.events.ControlListener;
|
||
|
|
import org.eclipse.swt.events.SelectionEvent;
|
||
|
|
import org.eclipse.swt.events.SelectionListener;
|
||
|
|
import org.eclipse.swt.events.TreeEvent;
|
||
|
|
import org.eclipse.swt.events.TreeListener;
|
||
|
|
import org.eclipse.swt.graphics.GC;
|
||
|
|
import org.eclipse.swt.graphics.Point;
|
||
|
|
import org.eclipse.swt.graphics.Rectangle;
|
||
|
|
import org.eclipse.swt.widgets.Control;
|
||
|
|
import org.eclipse.swt.widgets.Event;
|
||
|
|
import org.eclipse.swt.widgets.Listener;
|
||
|
|
import org.eclipse.swt.widgets.TableItem;
|
||
|
|
import org.eclipse.swt.widgets.TreeColumn;
|
||
|
|
import org.eclipse.swt.widgets.TreeItem;
|
||
|
|
|
||
|
|
import com.common.comparison.Comparator;
|
||
|
|
import com.common.debug.Debug;
|
||
|
|
import com.common.util.IHashSet;
|
||
|
|
import com.common.util.IIterator;
|
||
|
|
import com.common.util.IList;
|
||
|
|
import com.common.util.LiteHashMap;
|
||
|
|
import com.common.util.LiteHashSet;
|
||
|
|
import com.common.util.LiteList;
|
||
|
|
import com.common.util.optimized.IntArray;
|
||
|
|
import com.foundation.tcv.client.view.IAbstractClientViewComponent;
|
||
|
|
import com.foundation.tcv.client.view.MultiResourceHolder;
|
||
|
|
import com.foundation.tcv.client.view.ResourceHolder;
|
||
|
|
import com.foundation.tcv.swt.ISimpleTreeTable;
|
||
|
|
import com.foundation.tcv.swt.SimpleTreeTableCellData;
|
||
|
|
import com.foundation.tcv.swt.client.cell.CellComponent;
|
||
|
|
import com.foundation.tcv.view.ViewMessage;
|
||
|
|
import com.foundation.view.JefColor;
|
||
|
|
import com.foundation.view.JefFont;
|
||
|
|
import com.foundation.view.JefImage;
|
||
|
|
import com.foundation.view.swt.SimpleTreeTable.ColumnData;
|
||
|
|
import com.foundation.view.swt.util.SwtUtilities;
|
||
|
|
|
||
|
|
public class SimpleTreeTable extends TreeComponent implements ISimpleTreeTable, TreeListener, ControlListener {
|
||
|
|
/** A holder for the value of the row background color. */
|
||
|
|
private MultiResourceHolder rowBackgroundColorHolder = new MultiResourceHolder(this);
|
||
|
|
/** A holder for the value of the row foreground color. */
|
||
|
|
private MultiResourceHolder rowForegroundColorHolder = new MultiResourceHolder(this);
|
||
|
|
/** A holder for the value of the row font. */
|
||
|
|
private MultiResourceHolder rowFontHolder = new MultiResourceHolder(this);
|
||
|
|
/** A flag to temporarily disable the delay when synchronizing the selection before sending the double click message. */
|
||
|
|
private boolean disableAutoSynchronizeDelay = false;
|
||
|
|
/** Whether the sorting is managed by the view versus the model. */
|
||
|
|
private boolean inViewSorting = true;
|
||
|
|
/** The last column to be locally sorted. This allows the user to reverse sort a column. */
|
||
|
|
private int lastSortedColumnIndex = -1;
|
||
|
|
/** Whether the last sorted column was reverse sorted. */
|
||
|
|
private boolean isSortReversed = false;
|
||
|
|
/** Fixes a bug in the table where by a double click event gets fired twice. */
|
||
|
|
private int lastDoubleClickTime = 0;
|
||
|
|
/** The tree item whose children we are waiting for. This will be non-null when sending a request for children. While this is non-null the tree will be disabled. */
|
||
|
|
private TreeItem waitingItem = null;
|
||
|
|
/** Used along with waitingItem to retain information on which control had the focus at the time a tree node is opened and the server is told to load the data. */
|
||
|
|
private Control focusControl = null;
|
||
|
|
/** Stores some data about the state of the component prior to rebuilding the component. This is normally null. */
|
||
|
|
private StateData stateData = null;
|
||
|
|
/** Sets whether or not the opening of a node causes the selection even to occur for that row. */
|
||
|
|
private boolean selectOnOpen = false;
|
||
|
|
/** The collection of visible columns ordered by their server side zero based index. */
|
||
|
|
private IList columns = new LiteList(20, 10);
|
||
|
|
/** The set of currently visible tree items. */
|
||
|
|
private LiteHashSet currentlyVisibleTreeItems = null;
|
||
|
|
/** Allows us to ignore resize events while creating items during initialization. */
|
||
|
|
private boolean suspendResizeEvents = false;
|
||
|
|
/** Allows us to ignore tree table editor updates while adding, removing, or moving items. */
|
||
|
|
private boolean suspendUpdateTreeEditors = false;
|
||
|
|
/** Allows us to ignore resize events for the column while resizing the columns. */
|
||
|
|
private boolean suspendColumnResizeEvents = false;
|
||
|
|
/** The desired row height. This may be set to a positive number if the measure item event need be used to set the row height. */
|
||
|
|
private int rowHeightValue = -1;
|
||
|
|
/** Whether resizeable columns will be called upon to fit the control's client space when the control resizes or a column resizes. */
|
||
|
|
private boolean autoFit = false;
|
||
|
|
/** Whether resizeable columns will be streched to fill available space during the control's initialization. */
|
||
|
|
private boolean fillOnInitialize = false;
|
||
|
|
/** Whether resizeable columns will be streched to fill available space after the control is resized. */
|
||
|
|
private boolean fillOnResize = false;
|
||
|
|
/** The last known width for the control's client area. This is used to avoid filling or fitting the columns unless the width of the control is altered. */
|
||
|
|
private int lastControlWidth = 0;
|
||
|
|
/** The minimum tree editor height for all tested tree editors. */
|
||
|
|
private int minimumTreeEditorHeight = -1;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Encapsulates the reusable parts of a tree node.
|
||
|
|
*/
|
||
|
|
protected class SimpleNodeData extends NodeData {
|
||
|
|
/** The mapping of cell components for each column for this row. This may be null if no cell components have been defined. */
|
||
|
|
public CellComponent[] cellComponents = null;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* SimpleNodeData constructor.
|
||
|
|
* @param objectId The identifier used to bind this node with the one on the server.
|
||
|
|
* @param hiddenDataCount The count of hidden data columns defined for the component.
|
||
|
|
*/
|
||
|
|
public SimpleNodeData(int objectId, int hiddenDataCount) {
|
||
|
|
super(objectId, hiddenDataCount);
|
||
|
|
}//SimpleNodeData()//
|
||
|
|
/**
|
||
|
|
* Registers the row object with the cell component for a given column.
|
||
|
|
* @param columnData The column with which to register.
|
||
|
|
* @param cellComponentId The component id for the cell. A value of -1 indicates that there is no component associated with the cell.
|
||
|
|
*/
|
||
|
|
public void registerCellComponent(ColumnData columnData, int cellComponentId) {
|
||
|
|
if(cellComponents == null) {
|
||
|
|
cellComponents = new CellComponent[getColumnCount()];
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if(cellComponents[columnData.serverColumnIndex] != null) {
|
||
|
|
cellComponents[columnData.serverColumnIndex].unregister(this);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Store the cell component id at the server index for the column.//
|
||
|
|
cellComponents[columnData.serverColumnIndex] = cellComponentId != -1 ? (CellComponent) getComponent(cellComponentId) : null;
|
||
|
|
|
||
|
|
if(cellComponents[columnData.serverColumnIndex] != null) {
|
||
|
|
cellComponents[columnData.serverColumnIndex].register(this);
|
||
|
|
}//if//
|
||
|
|
}//registerCellComponent()//
|
||
|
|
/**
|
||
|
|
* Unregisters the row object with the cell component for a given column.
|
||
|
|
* @param columnData The column with which to unregister.
|
||
|
|
*/
|
||
|
|
public void unregisterCellComponent(ColumnData columnData) {
|
||
|
|
if(cellComponents != null) {
|
||
|
|
registerCellComponent(columnData, -1);
|
||
|
|
}//if//
|
||
|
|
}//unregisterCellComponent()//
|
||
|
|
/**
|
||
|
|
* Unregisters the row object with the cell component for all columns.
|
||
|
|
*/
|
||
|
|
public void unregisterCellComponents() {
|
||
|
|
if(cellComponents != null) {
|
||
|
|
for(int index = 0; index < cellComponents.length; index++) {
|
||
|
|
if(cellComponents[index] != null) {
|
||
|
|
cellComponents[index].unregister(this);
|
||
|
|
cellComponents[index] = null;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//unregisterCellComponents()//
|
||
|
|
/**
|
||
|
|
* Gets the cell component for a given column and this row.
|
||
|
|
* @param columnData The column data representing the column.
|
||
|
|
* @return The cell component used to render the cell, or null if none is given.
|
||
|
|
*/
|
||
|
|
public CellComponent getCellComponent(ColumnData columnData) {
|
||
|
|
CellComponent result = null;
|
||
|
|
|
||
|
|
if(cellComponents != null) {
|
||
|
|
result = cellComponents[columnData.serverColumnIndex];
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getCellComponent()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent.NodeData#dispose()
|
||
|
|
*/
|
||
|
|
public void dispose() {
|
||
|
|
IList controlItems = getDisplayNodes();
|
||
|
|
|
||
|
|
//TODO: Is it necessary to unregister with the cell components?
|
||
|
|
|
||
|
|
if(controlItems != null) {
|
||
|
|
for(int index = 0, length = controlItems.getSize(); index < length; index++) {
|
||
|
|
((TreeItem) controlItems.get(index)).dispose();
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
super.dispose();
|
||
|
|
}//dispose()//
|
||
|
|
}//SimpleNodeData//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Encapsulates information about an expanded node in the tree.
|
||
|
|
*/
|
||
|
|
protected static class StateData {
|
||
|
|
/** A (potentially null) list of StateExpansionData instances, one for each root node which was expanded. */
|
||
|
|
private IList nodes = null;
|
||
|
|
/** The top item's identifying data. */
|
||
|
|
private String[] topItemPath = null;
|
||
|
|
|
||
|
|
public StateData() {
|
||
|
|
}//StateData()//
|
||
|
|
public void addNode(StateExpansionData node) {
|
||
|
|
if(nodes == null) {
|
||
|
|
nodes = new LiteList(node);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
nodes.add(node);
|
||
|
|
}//else//
|
||
|
|
}//addNode()//
|
||
|
|
public IList getNodes() {
|
||
|
|
return nodes;
|
||
|
|
}//getNodes()//
|
||
|
|
public String[] getTopItemPath() {
|
||
|
|
return topItemPath;
|
||
|
|
}//getTopItemPath()//
|
||
|
|
public void setTopItemPath(String[] topItemPath) {
|
||
|
|
this.topItemPath = topItemPath;
|
||
|
|
}//setTopItemPath()//
|
||
|
|
|
||
|
|
//The following methods were placed here to keep them out of the general population of methods in the SimpleTreeTable class (since they are all related to the state data anyhow).//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Recursively collects the expansion data for a tree item.
|
||
|
|
* @param parentData The parent expansion data (to which the children will be added).
|
||
|
|
* @param parentItem The parent item whose children will be examined to see if they are expanded.
|
||
|
|
*/
|
||
|
|
private static void collectChildExpansionData(StateExpansionData parentData, TreeItem parentItem) {
|
||
|
|
TreeItem[] items = parentItem.getItems();
|
||
|
|
|
||
|
|
for(int index = 0; index < items.length; index++) {
|
||
|
|
if(items[index].getExpanded()) {
|
||
|
|
StateExpansionData data = new StateExpansionData(items[index].getText());
|
||
|
|
|
||
|
|
parentData.addChild(data);
|
||
|
|
collectChildExpansionData(data, items[index]);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//collectChildExpansionData()//
|
||
|
|
/**
|
||
|
|
* Finds the first matching item in the tree
|
||
|
|
* @param items The tree items to start searching in.
|
||
|
|
* @param itemIdentifier The identifier text used to find the old top item in the new set of items.
|
||
|
|
* @param depth The depth of the search.
|
||
|
|
* @return Whether the top item was found.
|
||
|
|
*/
|
||
|
|
private static TreeItem findItem(TreeItem[] items, String[] itemIdentifier, int depth) {
|
||
|
|
TreeItem result = null;
|
||
|
|
|
||
|
|
//Search the items for a match at the current depth.//
|
||
|
|
if(items != null) {
|
||
|
|
for(int index = 0; (result == null) && (index < items.length); index++) {
|
||
|
|
String itemText = items[index].getText();
|
||
|
|
|
||
|
|
if(Comparator.getStringComparator().compare(itemText, itemIdentifier[depth]) == Comparator.EQUAL) {
|
||
|
|
result = items[index];
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//If we could find a match, then search its children for a match if necessary.//
|
||
|
|
if((result != null) && (depth + 1 < itemIdentifier.length)) {
|
||
|
|
TreeItem child = findItem(result.getItems(), itemIdentifier, depth + 1);
|
||
|
|
|
||
|
|
//If we couldn't find a matching child, then use the parent.//
|
||
|
|
if(child != null) {
|
||
|
|
result = child;
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//restoreTopItem()//
|
||
|
|
/**
|
||
|
|
* Recursively expands previously expanded nodes using the list of expanded nodes and the parent tree item.
|
||
|
|
* @param items The tree items to start searching in.
|
||
|
|
* @param stateExpansionData The collection of StateExpansionData instances which each represent a previously expanded node at this level of the tree.
|
||
|
|
*/
|
||
|
|
private static void expandItems(TreeItem[] items, IList stateExpansionData) {
|
||
|
|
if(stateExpansionData != null) {
|
||
|
|
IIterator iterator = stateExpansionData.iterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
StateExpansionData data = (StateExpansionData) iterator.next();
|
||
|
|
TreeItem item = expandItem(items, data);
|
||
|
|
|
||
|
|
if((item != null) && (data.getChildren() != null)) {
|
||
|
|
expandItems(item.getItems(), data.getChildren());
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//if//
|
||
|
|
}//expandItems()//
|
||
|
|
/**
|
||
|
|
* Expands the tree item that is represented by the given data.
|
||
|
|
* @param items The tree items to start searching in.
|
||
|
|
* @param data The expansion state data representing the previously expanded node.
|
||
|
|
* @return The tree item that was expanded because it matched the expansion data, or null if no matching node was found.
|
||
|
|
*/
|
||
|
|
private static TreeItem expandItem(TreeItem[] items, StateExpansionData data) {
|
||
|
|
TreeItem item = null;
|
||
|
|
|
||
|
|
if(items != null) {
|
||
|
|
for(int index = 0; (item == null) && (index < items.length); index++) {
|
||
|
|
String itemText = items[index].getText();
|
||
|
|
|
||
|
|
if(Comparator.getStringComparator().compare(itemText, data.getIdentifier()) == Comparator.EQUAL) {
|
||
|
|
item = items[index];
|
||
|
|
item.setExpanded(true);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return item;
|
||
|
|
}//expandItem()//
|
||
|
|
}//StateData//
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Encapsulates information about an expanded node in the tree.
|
||
|
|
*/
|
||
|
|
protected static class StateExpansionData {
|
||
|
|
/** The identifier which will be used to match this node when the view is rebuilt. This is not a perfect solution, but works 99% of the time. */
|
||
|
|
private String identifier = null;
|
||
|
|
/** A (potentially null) list of child StateExpansionData instances, one for each child of this node which was expanded. */
|
||
|
|
private IList children = null;
|
||
|
|
|
||
|
|
public StateExpansionData(String identifier) {
|
||
|
|
this.identifier = identifier;
|
||
|
|
}//StateExpansionData()//
|
||
|
|
public void addChild(StateExpansionData child) {
|
||
|
|
if(children == null) {
|
||
|
|
children = new LiteList(child);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
children.add(child);
|
||
|
|
}//else//
|
||
|
|
}//addChild()//
|
||
|
|
public String getIdentifier() {
|
||
|
|
return identifier;
|
||
|
|
}//getIdentifier()//
|
||
|
|
public IList getChildren() {
|
||
|
|
return children;
|
||
|
|
}//getChildren()//
|
||
|
|
}//StateExpansionData//
|
||
|
|
public static class ColumnMultiResourceHolder extends MultiResourceHolder {
|
||
|
|
private ColumnData column;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ColumnMultiResourceHolder constructor.
|
||
|
|
* @param component The component that will be notified when the resource value changes.
|
||
|
|
* @param column The column defining the holder.
|
||
|
|
*/
|
||
|
|
public ColumnMultiResourceHolder(IAbstractClientViewComponent component, ColumnData column) {
|
||
|
|
super(component);
|
||
|
|
this.column = column;
|
||
|
|
}//ColumnMultiResourceHolder()//
|
||
|
|
/**
|
||
|
|
* Gets the column the holder is a member of.
|
||
|
|
* @return The column defining the holder.
|
||
|
|
*/
|
||
|
|
public ColumnData getColumn() {
|
||
|
|
return column;
|
||
|
|
}//getColumn()//
|
||
|
|
}//ColumnMultiResourceHolder//
|
||
|
|
public static class ColumnResourceHolder extends ResourceHolder {
|
||
|
|
private ColumnData column;
|
||
|
|
|
||
|
|
/**
|
||
|
|
* ColumnMultiResourceHolder constructor.
|
||
|
|
* @param component The component that will be notified when the resource value changes.
|
||
|
|
* @param column The column defining the holder.
|
||
|
|
*/
|
||
|
|
public ColumnResourceHolder(IAbstractClientViewComponent component, ColumnData column) {
|
||
|
|
super(component);
|
||
|
|
this.column = column;
|
||
|
|
}//ColumnMultiResourceHolder()//
|
||
|
|
/**
|
||
|
|
* Gets the column the holder is a member of.
|
||
|
|
* @return The column defining the holder.
|
||
|
|
*/
|
||
|
|
public ColumnData getColumn() {
|
||
|
|
return column;
|
||
|
|
}//getColumn()//
|
||
|
|
}//ColumnResourceHolder//
|
||
|
|
/**
|
||
|
|
* Metadata and functionality attached to the column.
|
||
|
|
*/
|
||
|
|
protected class ColumnData {
|
||
|
|
/** A holder for the value of the cell background color. */
|
||
|
|
private ColumnMultiResourceHolder backgroundColorHolder = new ColumnMultiResourceHolder(SimpleTreeTable.this, this);
|
||
|
|
/** A holder for the value of the cell foreground color. */
|
||
|
|
private ColumnMultiResourceHolder foregroundColorHolder = new ColumnMultiResourceHolder(SimpleTreeTable.this, this);
|
||
|
|
/** A holder for the value of the cell foreground color. */
|
||
|
|
private ColumnMultiResourceHolder fontHolder = new ColumnMultiResourceHolder(SimpleTreeTable.this, this);
|
||
|
|
/** A holder for the value of the cell image. */
|
||
|
|
private ColumnMultiResourceHolder cellImageHolder = new ColumnMultiResourceHolder(SimpleTreeTable.this, this);
|
||
|
|
/** A holder for the value of the column's tool tip text. */
|
||
|
|
private ColumnResourceHolder toolTipTextHolder = new ColumnResourceHolder(SimpleTreeTable.this, this);
|
||
|
|
/** A holder for the value of the column's header text. */
|
||
|
|
private ColumnResourceHolder headerTextHolder = new ColumnResourceHolder(SimpleTreeTable.this, this);
|
||
|
|
/** A holder for the value of the column's header image. */
|
||
|
|
private ColumnResourceHolder headerImageHolder = new ColumnResourceHolder(SimpleTreeTable.this, this);
|
||
|
|
/** The server side column index for this column. This will never change once the column is initialized. */
|
||
|
|
private int serverColumnIndex = 0;
|
||
|
|
/** The table column being served. */
|
||
|
|
private TreeColumn column;
|
||
|
|
/** The set of TreeEditor instances mapped by the TreeItem they are serving. This is used primarily to release the editors when the are out of view. */
|
||
|
|
private LiteHashMap editorByTreeItem = new LiteHashMap(50);
|
||
|
|
/** The minimum width of the column. */
|
||
|
|
private int minimumWidth = 20;
|
||
|
|
|
||
|
|
public ColumnData(TreeColumn column, int serverColumnIndex) {
|
||
|
|
this.column = column;
|
||
|
|
this.serverColumnIndex = serverColumnIndex;
|
||
|
|
}//ColumnData()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent.ColumnData#dispose()
|
||
|
|
*/
|
||
|
|
public void dispose() {
|
||
|
|
IIterator iterator = editorByTreeItem.valueIterator();
|
||
|
|
|
||
|
|
backgroundColorHolder.release();
|
||
|
|
foregroundColorHolder.release();
|
||
|
|
fontHolder.release();
|
||
|
|
cellImageHolder.release();
|
||
|
|
toolTipTextHolder.release();
|
||
|
|
headerTextHolder.release();
|
||
|
|
headerImageHolder.release();
|
||
|
|
|
||
|
|
if(column != null && !column.isDisposed()) {
|
||
|
|
column.dispose();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
TableEditor editor = (TableEditor) iterator.next();
|
||
|
|
|
||
|
|
editor.dispose();
|
||
|
|
//TODO: Dispose the control? We should actually be removing the column from the row object since it tracks the array of cell components, one per column.
|
||
|
|
}//while//
|
||
|
|
}//dispose()//
|
||
|
|
/**
|
||
|
|
* Forces the renderers to layout or reposition.
|
||
|
|
*/
|
||
|
|
public void layoutRenderers() {
|
||
|
|
IIterator iterator = editorByTreeItem.valueIterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
((TreeEditor) iterator.next()).layout();
|
||
|
|
}//while//
|
||
|
|
}//layoutRenderers()//
|
||
|
|
/**
|
||
|
|
* Gets the tree editor associated with the given row object.
|
||
|
|
* @param treeItem The tree item whose editor should be retrieved.
|
||
|
|
* @return The editor, or null if the row does not have an editor for this column.
|
||
|
|
*/
|
||
|
|
public TreeEditor getTreeEditor(TreeItem treeItem) {
|
||
|
|
return (TreeEditor) editorByTreeItem.get(treeItem);
|
||
|
|
}//getTreeEditor()//
|
||
|
|
/**
|
||
|
|
* Sets the tree editor associated with the given row object.
|
||
|
|
* @param treeItem The tree item whose editor should be set.
|
||
|
|
* @param treeEditor The editor, or null if the row does not have an editor for this column.
|
||
|
|
*/
|
||
|
|
public void setTreeEditor(TreeItem treeItem, TreeEditor treeEditor) {
|
||
|
|
editorByTreeItem.put(treeItem, treeEditor);
|
||
|
|
}//setTreeEditor()//
|
||
|
|
/**
|
||
|
|
* Removes and returns the tree editor associated with the given row object.
|
||
|
|
* @param treeItem The tree item whose editor should be removed.
|
||
|
|
* @return The editor, or null if the row does not have an editor for this column.
|
||
|
|
*/
|
||
|
|
public TreeEditor removeTreeEditor(TreeItem treeItem) {
|
||
|
|
return (TreeEditor) editorByTreeItem.remove(treeItem);
|
||
|
|
}//getTreeEditor()//
|
||
|
|
/**
|
||
|
|
* Gets the column's minimum width.
|
||
|
|
* @return The minimum number of pixels of width for the column.
|
||
|
|
*/
|
||
|
|
public int getMinimumWidth() {
|
||
|
|
return minimumWidth;
|
||
|
|
}//getMinimumWidth()//
|
||
|
|
/**
|
||
|
|
* Sets the column's minimum width.
|
||
|
|
* @param width The minimum number of pixels of width for the column.
|
||
|
|
*/
|
||
|
|
public void setMinimumWidth(int minimumWidth) {
|
||
|
|
if(this.minimumWidth != minimumWidth) {
|
||
|
|
this.minimumWidth = minimumWidth;
|
||
|
|
|
||
|
|
if(column.getWidth() < minimumWidth) {
|
||
|
|
column.setWidth(minimumWidth);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//setMinimumWidth()//
|
||
|
|
/**
|
||
|
|
* Sets the tool tip text which can either be a resource reference or a string.
|
||
|
|
* @param toolTipText The tool tip.
|
||
|
|
*/
|
||
|
|
public void setToolTipText(Object toolTipText) {
|
||
|
|
toolTipTextHolder.setValue(toolTipText);
|
||
|
|
}//setToolTipText()//
|
||
|
|
/**
|
||
|
|
* Gets the index of this column.
|
||
|
|
* @return The column's zero based index.
|
||
|
|
*/
|
||
|
|
public int getColumnIndex() {
|
||
|
|
int result = -1;
|
||
|
|
|
||
|
|
for(int index = 0; (result == -1) && (index < getSwtTree().getColumnCount()); index++) {
|
||
|
|
if(getSwtTree().getColumn(index) == column) {
|
||
|
|
result = index;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getColumnIndex()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.AbstractComponent#internalResourceHolderChanged(com.foundation.tcv.client.view.MultiResourceHolder, com.common.util.IHashSet, java.lang.Object, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void internalResourceHolderChanged(MultiResourceHolder resourceHolder, IHashSet rows, Object oldValue, Object newValue) {
|
||
|
|
int columnIndex = getColumnIndex();
|
||
|
|
|
||
|
|
if(resourceHolder == cellImageHolder) {
|
||
|
|
IIterator iterator = rows.iterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) iterator.next();
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyImage((JefImage) oldValue);
|
||
|
|
((TableItem) rowObject.getDisplayNodes().get(index)).setImage(columnIndex, createImage((JefImage) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//if//
|
||
|
|
else if(resourceHolder == backgroundColorHolder) {
|
||
|
|
IIterator iterator = rows.iterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) iterator.next();
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyColor((JefColor) oldValue);
|
||
|
|
((TableItem) rowObject.getDisplayNodes().get(index)).setBackground(columnIndex, createColor((JefColor) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == foregroundColorHolder) {
|
||
|
|
IIterator iterator = rows.iterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) iterator.next();
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyColor((JefColor) oldValue);
|
||
|
|
((TableItem) rowObject.getDisplayNodes().get(index)).setForeground(columnIndex, createColor((JefColor) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == fontHolder) {
|
||
|
|
IIterator iterator = rows.iterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) iterator.next();
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyFont((JefFont[]) oldValue);
|
||
|
|
((TableItem) rowObject.getDisplayNodes().get(index)).setFont(columnIndex, createFont((JefFont[]) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
Debug.log("Error: Unhandled resource holder change event found. ResourceHolder: " + resourceHolder);
|
||
|
|
}//else//
|
||
|
|
}//internalResourceHolderChanged()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.AbstractComponent#internalResourceHolderChanged(com.foundation.tcv.client.view.MultiResourceHolder, java.lang.Object, java.lang.Object, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void internalResourceHolderChanged(MultiResourceHolder resourceHolder, Object row, Object oldValue, Object newValue) {
|
||
|
|
int columnIndex = getColumnIndex();
|
||
|
|
|
||
|
|
if(resourceHolder == cellImageHolder) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) row;
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyImage((JefImage) oldValue);
|
||
|
|
((TableItem) rowObject.getDisplayNodes().get(index)).setImage(columnIndex, createImage((JefImage) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else if(resourceHolder == backgroundColorHolder) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) row;
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyColor((JefColor) oldValue);
|
||
|
|
((TableItem) rowObject.getDisplayNodes().get(index)).setBackground(columnIndex, createColor((JefColor) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == foregroundColorHolder) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) row;
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyColor((JefColor) oldValue);
|
||
|
|
((TableItem) rowObject.getDisplayNodes().get(index)).setForeground(columnIndex, createColor((JefColor) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == fontHolder) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) row;
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyFont((JefFont[]) oldValue);
|
||
|
|
((TableItem) rowObject.getDisplayNodes().get(index)).setFont(columnIndex, createFont((JefFont[]) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
Debug.log("Error: Unhandled resource holder change event found. ResourceHolder: " + resourceHolder);
|
||
|
|
}//else//
|
||
|
|
}//internalResourceHolderChanged()//
|
||
|
|
/**
|
||
|
|
* Called by a resource holder when the held value changes either due to a new value being set, or due to a resource being changed.
|
||
|
|
* @param resourceHolder The resource holder that is sending the notification.
|
||
|
|
* @param oldValue The old value for the resource.
|
||
|
|
* @param newValue The new value for the resource.
|
||
|
|
*/
|
||
|
|
protected void internalResourceHolderChanged(ResourceHolder resourceHolder, Object oldValue, Object newValue, int flags) {
|
||
|
|
if(resourceHolder == toolTipTextHolder) {
|
||
|
|
column.setToolTipText((String) toolTipTextHolder.getValue());
|
||
|
|
}//if//
|
||
|
|
else if(resourceHolder == headerTextHolder) {
|
||
|
|
column.setText((String) newValue);
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == headerImageHolder) {
|
||
|
|
destroyImage((JefImage) oldValue);
|
||
|
|
column.setImage(createImage((JefImage) newValue));
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
Debug.log("Error: Unhandled resource holder change event found. ResourceHolder: " + resourceHolder);
|
||
|
|
}//else//
|
||
|
|
}//internalResourceHolderChanged()//
|
||
|
|
}//ColumnData//
|
||
|
|
/**
|
||
|
|
* SimpleTable constructor.
|
||
|
|
*/
|
||
|
|
public SimpleTreeTable() {
|
||
|
|
super();
|
||
|
|
}//SimpleTreeTable()//
|
||
|
|
/**
|
||
|
|
* Gets the column with the given index.
|
||
|
|
* @param serverColumnIndex The server side zero based index for the desired column.
|
||
|
|
* @return The column data representing the column.
|
||
|
|
*/
|
||
|
|
protected ColumnData getColumn(int serverColumnIndex) {
|
||
|
|
return (ColumnData) columns.get(serverColumnIndex);
|
||
|
|
}//getColumn()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#getColumnCount()
|
||
|
|
*/
|
||
|
|
protected int getColumnCount() {
|
||
|
|
return getSwtTree().getColumnCount();
|
||
|
|
}//getColumnCount()//
|
||
|
|
/**
|
||
|
|
* Gets the SWT tree that represents this simple tree table.
|
||
|
|
* @return The SWT tree providing visualization for this simple tree table.
|
||
|
|
*/
|
||
|
|
public org.eclipse.swt.widgets.Tree getSwtTree() {
|
||
|
|
return (org.eclipse.swt.widgets.Tree) getSwtWidget();
|
||
|
|
}//getSwtTree()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.view.IViewComponent#viewInitialize()
|
||
|
|
*/
|
||
|
|
protected void internalViewInitialize() {
|
||
|
|
getSwtTree().addSelectionListener(this);
|
||
|
|
getSwtTree().addTreeListener(this);
|
||
|
|
getSwtTree().addControlListener(this);
|
||
|
|
getSwtTree().getVerticalBar().addSelectionListener(this);
|
||
|
|
getSwtTree().addListener(SWT.Resize, new Listener() {
|
||
|
|
public void handleEvent(Event event) {
|
||
|
|
if(getAutoFit()) {
|
||
|
|
int controlWidth = getSwtTree().getClientArea().width;
|
||
|
|
|
||
|
|
if(controlWidth != lastControlWidth) {
|
||
|
|
lastControlWidth = controlWidth;
|
||
|
|
fit();
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
//Debug.log("Widthx: " + getSwtTree().getClientArea().width);
|
||
|
|
}//handleEvent()//
|
||
|
|
});
|
||
|
|
getSwtTree().addListener(SWT.MeasureItem, new Listener() {
|
||
|
|
boolean isFirstCall = true;
|
||
|
|
|
||
|
|
public void handleEvent(Event event) {
|
||
|
|
switch(event.type) {
|
||
|
|
case SWT.MeasureItem: {
|
||
|
|
if(rowHeightValue > 0) {
|
||
|
|
event.height = rowHeightValue;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
TreeItem item = (TreeItem) event.item;
|
||
|
|
//TableRowObject rowObject = (TableRowObject) item.getData();
|
||
|
|
ColumnData column = (ColumnData) getColumn(event.index);
|
||
|
|
TreeEditor editor = column.getTreeEditor(item);
|
||
|
|
|
||
|
|
if(minimumTreeEditorHeight > event.height) {
|
||
|
|
event.height = minimumTreeEditorHeight;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Note: minimumTreeEditorHeight should suffice, but are we properly updating it as the editors change content?
|
||
|
|
//TODO: Cache the editor's control's preferred height? It is unlikely to change (but I suppose it is possible for things like multi line text controls without scroll bars?).
|
||
|
|
if(editor != null) {
|
||
|
|
if(event.height < editor.minimumHeight) {
|
||
|
|
event.height = editor.minimumHeight;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
Control control = editor.getEditor();
|
||
|
|
Point point = control.computeSize(event.width, event.height);
|
||
|
|
|
||
|
|
if(point.y > event.height) {
|
||
|
|
event.height = point.y;
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
//TODO: This code should not be necessary. It forces the repaint of the control since we are having a problem with the widgets not being the right size or position when first rendered.
|
||
|
|
if(isFirstCall) {
|
||
|
|
isFirstCall = false;
|
||
|
|
|
||
|
|
getSwtTree().getDisplay().asyncExec(new Runnable() {
|
||
|
|
public void run() {
|
||
|
|
// SwtUtilities.setRedraw(getSwtTree(), true);
|
||
|
|
// getSwtTree().redraw();
|
||
|
|
layoutRenderers();
|
||
|
|
}//run()//
|
||
|
|
});
|
||
|
|
}//if//
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
}//switch//
|
||
|
|
}//handleEvent()//
|
||
|
|
});
|
||
|
|
getSwtTree().addListener(SWT.PaintItem, new Listener() {
|
||
|
|
public void handleEvent(Event event) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) ((TreeItem) event.item).getData();
|
||
|
|
TreeColumn column = getSwtTree().getColumn(event.index);
|
||
|
|
ColumnData columnData = (ColumnData) column.getData();
|
||
|
|
CellComponent cellComponent = rowObject.getCellComponent(columnData);
|
||
|
|
|
||
|
|
//Only paint the cell if there is a cell component and it supports painting the cell.//
|
||
|
|
if((cellComponent != null) && (cellComponent.supportsPaint()) && (cellComponent.usePaint())) {
|
||
|
|
GC gc = event.gc;
|
||
|
|
Rectangle rectangle = gc.getClipping();
|
||
|
|
|
||
|
|
//Set the clip area so the component knows where to draw.//
|
||
|
|
gc.setClipping(event.x, event.y, column.getWidth(), event.height);
|
||
|
|
//Send GC to the column's cell component.//
|
||
|
|
cellComponent.paintCell(rowObject, gc);
|
||
|
|
//Reset the clipping area.//
|
||
|
|
gc.setClipping(rectangle);
|
||
|
|
}//if//
|
||
|
|
}//handleEvent()//
|
||
|
|
});
|
||
|
|
|
||
|
|
super.internalViewInitialize();
|
||
|
|
|
||
|
|
if(getAutoFit()) {
|
||
|
|
fit();
|
||
|
|
}//if//
|
||
|
|
else if(getFillOnInitialize()) {
|
||
|
|
fill();
|
||
|
|
}//else if//
|
||
|
|
}//internalViewInitialize()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.view.IViewComponent#viewRelease()
|
||
|
|
*/
|
||
|
|
protected void internalViewRelease() {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
getSwtTree().removeControlListener(this);
|
||
|
|
getSwtTree().removeSelectionListener(this);
|
||
|
|
getSwtTree().removeTreeListener(this);
|
||
|
|
getSwtTree().getVerticalBar().removeSelectionListener(this);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
rowBackgroundColorHolder.release();
|
||
|
|
rowForegroundColorHolder.release();
|
||
|
|
rowFontHolder.release();
|
||
|
|
|
||
|
|
for(int index = 0; index < columns.getSize(); index++) {
|
||
|
|
((ColumnData) columns.get(index)).dispose();
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
super.internalViewRelease();
|
||
|
|
}//internalViewRelease()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.AbstractComponent#internalViewSynchronize()
|
||
|
|
*/
|
||
|
|
protected void internalViewSynchronize() {
|
||
|
|
super.internalViewSynchronize();
|
||
|
|
}//internalViewSynchronize()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.Component#internalOnLinkInvoked(int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void internalOnLinkInvoked(int linkTarget, Object data) {
|
||
|
|
switch(linkTarget) {
|
||
|
|
case LINK_TARGET_FILL: {
|
||
|
|
lastControlWidth = getSwtTree().getClientArea().width;
|
||
|
|
fill();
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case LINK_TARGET_FIT: {
|
||
|
|
lastControlWidth = getSwtTree().getClientArea().width;
|
||
|
|
fit();
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
default: {
|
||
|
|
super.internalOnLinkInvoked(linkTarget, data);
|
||
|
|
break;
|
||
|
|
}//default//
|
||
|
|
}//switch//
|
||
|
|
}//internalOnLinkInvoked()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.AbstractComponent#internalResourceHolderChanged(com.foundation.tcv.client.view.MultiResourceHolder, com.common.util.IHashSet, java.lang.Object, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void internalResourceHolderChanged(MultiResourceHolder resourceHolder, IHashSet rows, Object oldValue, Object newValue) {
|
||
|
|
if(resourceHolder instanceof ColumnMultiResourceHolder) {
|
||
|
|
ColumnData column = ((ColumnMultiResourceHolder) resourceHolder).column;
|
||
|
|
|
||
|
|
column.internalResourceHolderChanged(resourceHolder, rows, oldValue, newValue);
|
||
|
|
}//if//
|
||
|
|
else if(resourceHolder == rowBackgroundColorHolder) {
|
||
|
|
IIterator iterator = rows.iterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) iterator.next();
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyColor((JefColor) oldValue);
|
||
|
|
((TreeItem) rowObject.getDisplayNodes().get(index)).setBackground(createColor((JefColor) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == rowForegroundColorHolder) {
|
||
|
|
IIterator iterator = rows.iterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) iterator.next();
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyColor((JefColor) oldValue);
|
||
|
|
((TreeItem) rowObject.getDisplayNodes().get(index)).setForeground(createColor((JefColor) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == rowFontHolder) {
|
||
|
|
IIterator iterator = rows.iterator();
|
||
|
|
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) iterator.next();
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyFont((JefFont[]) oldValue);
|
||
|
|
((TreeItem) rowObject.getDisplayNodes().get(index)).setFont(createFont((JefFont[]) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
super.internalResourceHolderChanged(resourceHolder, rows, oldValue, newValue);
|
||
|
|
}//else//
|
||
|
|
}//internalResourceHolderChanged()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.AbstractComponent#internalResourceHolderChanged(com.foundation.tcv.client.view.MultiResourceHolder, java.lang.Object, java.lang.Object, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void internalResourceHolderChanged(MultiResourceHolder resourceHolder, Object row, Object oldValue, Object newValue) {
|
||
|
|
if(resourceHolder instanceof ColumnMultiResourceHolder) {
|
||
|
|
ColumnData column = ((ColumnMultiResourceHolder) resourceHolder).column;
|
||
|
|
|
||
|
|
column.internalResourceHolderChanged(resourceHolder, row, oldValue, newValue);
|
||
|
|
}//if//
|
||
|
|
else if(resourceHolder == rowBackgroundColorHolder) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) row;
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyColor((JefColor) oldValue);
|
||
|
|
((TreeItem) rowObject.getDisplayNodes().get(index)).setBackground(createColor((JefColor) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == rowForegroundColorHolder) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) row;
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyColor((JefColor) oldValue);
|
||
|
|
((TreeItem) rowObject.getDisplayNodes().get(index)).setForeground(createColor((JefColor) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
else if(resourceHolder == rowFontHolder) {
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) row;
|
||
|
|
|
||
|
|
if(rowObject.getDisplayNodes() != null) {
|
||
|
|
for(int index = 0; index < rowObject.getDisplayNodes().getSize(); index++) {
|
||
|
|
destroyFont((JefFont[]) oldValue);
|
||
|
|
((TreeItem) rowObject.getDisplayNodes().get(index)).setFont(createFont((JefFont[]) newValue));
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//else if//
|
||
|
|
else {
|
||
|
|
super.internalResourceHolderChanged(resourceHolder, row, oldValue, newValue);
|
||
|
|
}//else//
|
||
|
|
}//internalResourceHolderChanged()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.Component#internalResourceHolderChanged(com.foundation.tcv.client.view.ResourceHolder, java.lang.Object, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void internalResourceHolderChanged(ResourceHolder resourceHolder, Object oldValue, Object newValue, int flags) {
|
||
|
|
if(resourceHolder instanceof ColumnResourceHolder) {
|
||
|
|
ColumnData column = ((ColumnResourceHolder) resourceHolder).column;
|
||
|
|
|
||
|
|
column.internalResourceHolderChanged(resourceHolder, oldValue, newValue, flags);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
super.internalResourceHolderChanged(resourceHolder, oldValue, newValue, flags);
|
||
|
|
}//else//
|
||
|
|
}//internalResourceHolderChanged()//
|
||
|
|
/**
|
||
|
|
* Fills the available space by expanding all resizeable columns.
|
||
|
|
*/
|
||
|
|
protected void fill() {
|
||
|
|
int width = lastControlWidth;
|
||
|
|
TreeColumn[] columns = getSwtTree().getColumns();
|
||
|
|
int totalColumnWidth = 0;
|
||
|
|
int increaseWidth = 0;
|
||
|
|
int extraIncreaseWidth = 0;
|
||
|
|
int resizeableColumnCount = 0;
|
||
|
|
|
||
|
|
for(int index = 0; index < columns.length; index++) {
|
||
|
|
totalColumnWidth += columns[index].getWidth();
|
||
|
|
|
||
|
|
if(columns[index].getResizable()) {
|
||
|
|
resizeableColumnCount++;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
if(width > totalColumnWidth) {
|
||
|
|
if(resizeableColumnCount > 0) {
|
||
|
|
SwtUtilities.setRedraw(getSwtTree(), false);
|
||
|
|
suspendColumnResizeEvents = true;
|
||
|
|
|
||
|
|
try {
|
||
|
|
increaseWidth = (int) Math.floor((width - totalColumnWidth) / (double) resizeableColumnCount);
|
||
|
|
extraIncreaseWidth = (width - totalColumnWidth) % resizeableColumnCount;
|
||
|
|
|
||
|
|
for(int index = 0; index < columns.length; index++) {
|
||
|
|
if(columns[index].getResizable()) {
|
||
|
|
int newWidth = columns[index].getWidth() + increaseWidth;
|
||
|
|
|
||
|
|
//Add one to the new width for the extra pixels.//
|
||
|
|
if(resizeableColumnCount <= extraIncreaseWidth) {
|
||
|
|
newWidth++;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Set the new width and decrement the resizeable column count so we track how many columns to add the extra pixel to.//
|
||
|
|
columns[index].setWidth(newWidth);
|
||
|
|
resizeableColumnCount--;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
suspendColumnResizeEvents = false;
|
||
|
|
SwtUtilities.setRedraw(getSwtTree(), true);
|
||
|
|
getSwtTree().redraw();
|
||
|
|
}//finally//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//fill()//
|
||
|
|
/**
|
||
|
|
* Fits the resizeable columns to fill all available table space without needing a horizontal scroll bar.
|
||
|
|
*/
|
||
|
|
protected void fit() {
|
||
|
|
fit(null);
|
||
|
|
}//fit()//
|
||
|
|
/**
|
||
|
|
* Fits the resizeable columns to fill all available table space without needing a horizontal scroll bar.
|
||
|
|
* @param ignoredColumn The column to be ignored in the fit operation. Note that there must be at least 2 resizeable columns for this algorithm to work, and no column should be resized less than the minimum size.
|
||
|
|
*/
|
||
|
|
protected void fit(TreeColumn ignoredColumn) {
|
||
|
|
TreeColumn[] columns = getSwtTree().getColumns();
|
||
|
|
int extraWidth = 0;
|
||
|
|
LiteList resizeableColumns = new LiteList(columns.length);
|
||
|
|
int totalMinimumWidth = 0;
|
||
|
|
int currentColumnWidthTotal = 0;
|
||
|
|
int fixedWidth = 0;
|
||
|
|
int controlWidth = lastControlWidth;
|
||
|
|
int availableWidth = controlWidth - extraWidth;
|
||
|
|
|
||
|
|
if(lastControlWidth == 0) {
|
||
|
|
lastControlWidth = getSwtControl().getBounds().width;
|
||
|
|
controlWidth = lastControlWidth;
|
||
|
|
availableWidth = controlWidth - extraWidth;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
for(int index = 0; index < columns.length; index++) {
|
||
|
|
if((columns[index] != ignoredColumn) && (columns[index].getResizable())) {
|
||
|
|
resizeableColumns.add(columns[index]);
|
||
|
|
totalMinimumWidth += ((ColumnData) columns[index].getData()).getMinimumWidth();
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
if(!columns[index].getResizable()) {
|
||
|
|
fixedWidth += columns[index].getWidth();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
availableWidth -= columns[index].getWidth();
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
currentColumnWidthTotal += columns[index].getWidth();
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//The width of the control which can be divied up between the resizable columns.//
|
||
|
|
availableWidth -= totalMinimumWidth;
|
||
|
|
|
||
|
|
//Detect formatting errors that cannot be worked around.//
|
||
|
|
if((controlWidth > 0) && (resizeableColumns.getSize() > 0) && (availableWidth > 0) && (currentColumnWidthTotal != controlWidth)) {
|
||
|
|
int[] columnSizes = new int[resizeableColumns.getSize()];
|
||
|
|
int totalColumnSizes = 0;
|
||
|
|
int newTotalColumnSizes = 0;
|
||
|
|
|
||
|
|
//Record the previous sizes (minus the minimum width to make the equation simpler).//
|
||
|
|
for(int index = 0; index < resizeableColumns.getSize(); index++) {
|
||
|
|
TreeColumn next = (TreeColumn) resizeableColumns.get(index);
|
||
|
|
|
||
|
|
totalColumnSizes += (columnSizes[index] = (next.getWidth() - ((ColumnData) next.getData()).getMinimumWidth()));
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Resize the columns relative to their previous sizes.//
|
||
|
|
for(int index = 0; index < columnSizes.length; index++) {
|
||
|
|
TreeColumn next = (TreeColumn) resizeableColumns.get(index);
|
||
|
|
|
||
|
|
newTotalColumnSizes += (columnSizes[index] = ((int) Math.round((columnSizes[index] / (double) totalColumnSizes) * availableWidth)) + ((ColumnData) next.getData()).getMinimumWidth());
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
availableWidth += totalMinimumWidth;
|
||
|
|
//Debug.log("New Total Column Sizes: " + newTotalColumnSizes + " Control Width - Fixed Width: " + availableWidth);
|
||
|
|
|
||
|
|
//Adjust the column sizes to take up the exact right amount of space.//
|
||
|
|
for(int index = 0; (index < columnSizes.length) && (newTotalColumnSizes != availableWidth); index++) {
|
||
|
|
if(newTotalColumnSizes < availableWidth) {
|
||
|
|
//Debug.log("Adding");
|
||
|
|
columnSizes[index]++;
|
||
|
|
newTotalColumnSizes++;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
//Debug.log("Removing");
|
||
|
|
columnSizes[index]--;
|
||
|
|
newTotalColumnSizes--;
|
||
|
|
}//else//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
SwtUtilities.setRedraw(getSwtTree(), false);
|
||
|
|
suspendColumnResizeEvents = true;
|
||
|
|
|
||
|
|
try {
|
||
|
|
for(int index = 0; index < resizeableColumns.getSize(); index++) {
|
||
|
|
((TreeColumn) resizeableColumns.get(index)).setWidth(columnSizes[index]);
|
||
|
|
}//for//
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
suspendColumnResizeEvents = false;
|
||
|
|
SwtUtilities.setRedraw(getSwtTree(), true);
|
||
|
|
getSwtTree().redraw();
|
||
|
|
}//finally//
|
||
|
|
}//if//
|
||
|
|
else if((availableWidth < 0) && (ignoredColumn != null)) {
|
||
|
|
int minimumWidth = ((ColumnData) ignoredColumn.getData()).getMinimumWidth();
|
||
|
|
|
||
|
|
SwtUtilities.setRedraw(getSwtTree(), false);
|
||
|
|
suspendColumnResizeEvents = true;
|
||
|
|
|
||
|
|
try {
|
||
|
|
//Ensure all columns that can be resized are set to their minimum width.//
|
||
|
|
for(int index = 0; index < resizeableColumns.getSize(); index++) {
|
||
|
|
TreeColumn column = (TreeColumn) resizeableColumns.get(index);
|
||
|
|
|
||
|
|
column.setWidth(((ColumnData) column.getData()).getMinimumWidth());
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Adjust the ignored column to either its minimum width, or to fill available space.//
|
||
|
|
if((ignoredColumn.getWidth() - minimumWidth + availableWidth) > 0) {
|
||
|
|
ignoredColumn.setWidth(controlWidth - extraWidth - fixedWidth - totalMinimumWidth);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
ignoredColumn.setWidth(minimumWidth);
|
||
|
|
}//else//
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
suspendColumnResizeEvents = false;
|
||
|
|
SwtUtilities.setRedraw(getSwtTree(), true);
|
||
|
|
getSwtTree().redraw();
|
||
|
|
}//finally//
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
//Test Code//
|
||
|
|
//int newColumnWidthTotal = 0;
|
||
|
|
//for(int index = 0; index < columns.length; index++) {
|
||
|
|
// newColumnWidthTotal += columns[index].getWidth();
|
||
|
|
//}//for//
|
||
|
|
|
||
|
|
//Debug.log("Total column width: " + newColumnWidthTotal + " Control client width: " + getSwtTree().getClientArea().width + "\n");
|
||
|
|
}//fit()//
|
||
|
|
/**
|
||
|
|
* Gets the index of the column.
|
||
|
|
* @param column The column whose index is to be determined.
|
||
|
|
* @return The index of the column, or -1 if the column is not displayed by the tree.
|
||
|
|
*/
|
||
|
|
private int getColumnIndex(TreeColumn column) {
|
||
|
|
int result = -1;
|
||
|
|
TreeColumn[] columns = getSwtTree().getColumns();
|
||
|
|
|
||
|
|
for(int index = 0; (result == -1) && (index < columns.length); index++) {
|
||
|
|
if(columns[index] == column) {
|
||
|
|
result = index;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//getColumnIndex()//
|
||
|
|
/**
|
||
|
|
* Called when a column is added to the tree table.
|
||
|
|
* @param column The column added.
|
||
|
|
* @param columnIndex The initial index for the column. This will not change on the server, but may change on the client, so to preserve a mapping we must record it.
|
||
|
|
*/
|
||
|
|
protected void internalColumnAdded(final TreeColumn column, int columnIndex) {
|
||
|
|
ColumnData columnData = new ColumnData(column, columnIndex);
|
||
|
|
|
||
|
|
//Add the column data to the columns list and update the server column indices.//
|
||
|
|
if(columnIndex >= 0) {
|
||
|
|
columnData.serverColumnIndex = columnIndex;
|
||
|
|
columns.add(columnIndex, columnData);
|
||
|
|
|
||
|
|
for(int index = columnIndex + 1; index < columns.getSize(); index++) {
|
||
|
|
((ColumnData) columns.get(index)).serverColumnIndex++;
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
columnData.serverColumnIndex = columns.getSize();
|
||
|
|
columns.add(columnData);
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
//Set the initial width.//
|
||
|
|
column.setWidth(100);
|
||
|
|
//TODO: Make the moveable feature be configurable.
|
||
|
|
column.setMoveable(true);
|
||
|
|
//Add a listener to resize the columns to fit the space if we are performing auto-fit.//
|
||
|
|
column.addControlListener(new ControlListener() {
|
||
|
|
public void controlResized(ControlEvent event) {
|
||
|
|
if(isInitialized() && !suspendColumnResizeEvents) {
|
||
|
|
if(((ColumnData) column.getData()).getMinimumWidth() > column.getWidth()) {
|
||
|
|
//Enforce the minimum width.//
|
||
|
|
column.setWidth(((ColumnData) column.getData()).getMinimumWidth());
|
||
|
|
}//if//
|
||
|
|
else if(autoFit) {
|
||
|
|
fit(column);
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
}//controlResized()//
|
||
|
|
public void controlMoved(ControlEvent event) {
|
||
|
|
}//controlMoved()//
|
||
|
|
});
|
||
|
|
//Add a sorting handler to the column.//
|
||
|
|
column.addSelectionListener(new SelectionListener() {
|
||
|
|
public void widgetDefaultSelected(SelectionEvent e) {
|
||
|
|
widgetSelected(e);
|
||
|
|
}//widgetDefaultSelected()//
|
||
|
|
public void widgetSelected(SelectionEvent e) {
|
||
|
|
if((inViewSorting) && (getSwtTree().getColumnCount() > 1)) {
|
||
|
|
int columnCount = getSwtTree().getColumnCount();
|
||
|
|
int columnIndex = getColumnIndex(column);
|
||
|
|
TreeItem[] mapping = getSwtTree().getItems();
|
||
|
|
boolean reverse = isSortReversed = (columnIndex == lastSortedColumnIndex ? !isSortReversed : false);
|
||
|
|
|
||
|
|
if(getSwtTree().getItemCount() > 0) {
|
||
|
|
boolean done = false;
|
||
|
|
|
||
|
|
//Perform a bubble sort on the tree items.//
|
||
|
|
while(!done) {
|
||
|
|
String text1 = mapping[0].getText(columnIndex);
|
||
|
|
|
||
|
|
done = true;
|
||
|
|
|
||
|
|
for(int index = 1; index < mapping.length; index++) {
|
||
|
|
String text2 = mapping[index].getText(columnIndex);
|
||
|
|
|
||
|
|
if(requiresSwap(text1, text2, reverse)) {
|
||
|
|
TreeItem temp = mapping[index];
|
||
|
|
|
||
|
|
mapping[index] = mapping[index - 1];
|
||
|
|
mapping[index - 1] = temp;
|
||
|
|
done = false;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
text1 = text2;
|
||
|
|
}//for//
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
SwtUtilities.setRedraw(getSwtComposite(), false);
|
||
|
|
|
||
|
|
try {
|
||
|
|
//Rebuild the tree items since we can't reuse them.//
|
||
|
|
for(int index = 0; index < mapping.length; index++) {
|
||
|
|
TreeItem treeItem = (TreeItem) mapping[index];
|
||
|
|
TreeItem newTreeItem = new TreeItem(getSwtTree(), treeItem.getStyle(), index);
|
||
|
|
|
||
|
|
newTreeItem.setChecked(treeItem.getChecked());
|
||
|
|
newTreeItem.setGrayed(treeItem.getGrayed());
|
||
|
|
newTreeItem.setText(treeItem.getText());
|
||
|
|
newTreeItem.setImage(treeItem.getImage());
|
||
|
|
newTreeItem.setBackground(treeItem.getBackground());
|
||
|
|
newTreeItem.setForeground(treeItem.getForeground());
|
||
|
|
newTreeItem.setFont(treeItem.getFont());
|
||
|
|
newTreeItem.setData(treeItem.getData());
|
||
|
|
|
||
|
|
for(int column = 0; column < columnCount; column++) {
|
||
|
|
newTreeItem.setText(column, treeItem.getText(column));
|
||
|
|
newTreeItem.setImage(column, treeItem.getImage(column));
|
||
|
|
newTreeItem.setBackground(column, treeItem.getBackground(column));
|
||
|
|
newTreeItem.setForeground(column, treeItem.getForeground(column));
|
||
|
|
newTreeItem.setFont(column, treeItem.getFont(column));
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//The data can be null if this is a temporary standin for possible children.//
|
||
|
|
if(treeItem.getData() != null) {
|
||
|
|
//Update the mapping to/from the node data.//
|
||
|
|
((NodeData) treeItem.getData()).getDisplayNodes().replace(treeItem, newTreeItem);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Sort the children.//
|
||
|
|
sortChildren(newTreeItem, treeItem, columnIndex, columnCount, reverse);
|
||
|
|
//Dispose of the child.//
|
||
|
|
treeItem.dispose();
|
||
|
|
}//for//
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
SwtUtilities.setRedraw(getSwtComposite(), true);
|
||
|
|
}//finally//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Save the last sorted column index so we know when to reverse the order of the sort.//
|
||
|
|
lastSortedColumnIndex = columnIndex;
|
||
|
|
getSwtTree().setSortColumn(column);
|
||
|
|
getSwtTree().setSortDirection(reverse ? SWT.UP : SWT.DOWN);
|
||
|
|
}//if//
|
||
|
|
}//widgetSelected()//
|
||
|
|
});
|
||
|
|
column.setData(columnData);
|
||
|
|
}//internalColumnAdded()//
|
||
|
|
/**
|
||
|
|
* Sorts the children of a tree item.
|
||
|
|
* @param newParent The new parent item which doesn't have children yet.
|
||
|
|
* @param oldParent The old parent item which may have unsorted children.
|
||
|
|
* @param columnIndex The index of the column we are sorting by.
|
||
|
|
* @param columnCount The number of columns in the control.
|
||
|
|
* @param reverse Whether the sort should be reversed.
|
||
|
|
*/
|
||
|
|
private void sortChildren(TreeItem newParent, TreeItem oldParent, int columnIndex, int columnCount, boolean reverse) {
|
||
|
|
if(oldParent.getItemCount() > 0) {
|
||
|
|
boolean done = false;
|
||
|
|
TreeItem[] mapping = oldParent.getItems();
|
||
|
|
|
||
|
|
//Perform a bubble sort on the table items.//
|
||
|
|
while(!done) {
|
||
|
|
String text1 = mapping[0].getText(columnIndex);
|
||
|
|
|
||
|
|
done = true;
|
||
|
|
|
||
|
|
for(int index = 1; index < mapping.length; index++) {
|
||
|
|
String text2 = mapping[index].getText(columnIndex);
|
||
|
|
int compare = text1.compareTo(text2);
|
||
|
|
|
||
|
|
if((reverse && (compare > 0)) || (!reverse && (compare < 0))) {
|
||
|
|
TreeItem temp = mapping[index];
|
||
|
|
|
||
|
|
mapping[index] = mapping[index - 1];
|
||
|
|
mapping[index - 1] = temp;
|
||
|
|
done = false;
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
text1 = text2;
|
||
|
|
}//for//
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
//Rebuild the tree items since we can't reuse them.//
|
||
|
|
for(int index = 0; index < mapping.length; index++) {
|
||
|
|
TreeItem treeItem = (TreeItem) mapping[index];
|
||
|
|
TreeItem newTreeItem = new TreeItem(newParent, treeItem.getStyle(), index);
|
||
|
|
|
||
|
|
newTreeItem.setChecked(treeItem.getChecked());
|
||
|
|
newTreeItem.setGrayed(treeItem.getGrayed());
|
||
|
|
newTreeItem.setText(treeItem.getText());
|
||
|
|
newTreeItem.setImage(treeItem.getImage());
|
||
|
|
newTreeItem.setBackground(treeItem.getBackground());
|
||
|
|
newTreeItem.setForeground(treeItem.getForeground());
|
||
|
|
newTreeItem.setFont(treeItem.getFont());
|
||
|
|
newTreeItem.setData(treeItem.getData());
|
||
|
|
|
||
|
|
for(int column = 0; column < columnCount; column++) {
|
||
|
|
newTreeItem.setText(column, treeItem.getText(column));
|
||
|
|
newTreeItem.setImage(column, treeItem.getImage(column));
|
||
|
|
newTreeItem.setBackground(column, treeItem.getBackground(column));
|
||
|
|
newTreeItem.setForeground(column, treeItem.getForeground(column));
|
||
|
|
newTreeItem.setFont(column, treeItem.getFont(column));
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//The data can be null if this is a temporary standin for possible children.//
|
||
|
|
if(treeItem.getData() != null) {
|
||
|
|
//Update the mapping to/from the node data.//
|
||
|
|
((NodeData) treeItem.getData()).getDisplayNodes().replace(treeItem, newTreeItem);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Sort the children.//
|
||
|
|
sortChildren(newTreeItem, treeItem, columnIndex, columnCount, reverse);
|
||
|
|
//Dispose of the child.//
|
||
|
|
treeItem.dispose();
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Set the expanded state of the new parent node.//
|
||
|
|
newParent.setExpanded(oldParent.getExpanded());
|
||
|
|
}//if//
|
||
|
|
}//sortChildren()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.AbstractComponent#internalProcessMessage(com.foundation.tcv.model.ViewMessage)
|
||
|
|
*/
|
||
|
|
public Object internalProcessMessage(ViewMessage viewMessage) {
|
||
|
|
Object retVal = null;
|
||
|
|
|
||
|
|
switch(viewMessage.getMessageNumber()) {
|
||
|
|
case MESSAGE_INITIALIZE: {
|
||
|
|
if(getSwtControl() == null) {
|
||
|
|
int[] data = (int[]) viewMessage.getMessageData();
|
||
|
|
int style = data[1];
|
||
|
|
|
||
|
|
//Link to the parent container.//
|
||
|
|
setContainer((Container) getComponent(data[0]));
|
||
|
|
getContainer().addComponent(this);
|
||
|
|
//Create the SWT widget.//
|
||
|
|
setSwtWidget(new org.eclipse.swt.widgets.Tree(getContainer().getSwtParent(), style | SWT.FULL_SELECTION));
|
||
|
|
getSwtWidget().setData(this);
|
||
|
|
//Set the allows multiple selections flag.//
|
||
|
|
setAllowMultiSelection((style & SWT.MULTI) > 0);
|
||
|
|
//Make sure the headers are visible by default.//
|
||
|
|
getSwtTree().setHeaderVisible(true);
|
||
|
|
|
||
|
|
//If the container has already been initialized then force the parent to re-layout so that this component will appear.//
|
||
|
|
if(getContainer().isInitialized()) {
|
||
|
|
getContainer().getSwtComposite().layout(true, true);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_ROW_HEIGHT: {
|
||
|
|
Integer height = (Integer) viewMessage.getMessageData();
|
||
|
|
|
||
|
|
if(height != null) {
|
||
|
|
rowHeightValue = height.intValue();
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
rowHeightValue = -1;
|
||
|
|
}//else//
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_IN_VIEW_SORTING: {
|
||
|
|
//Warning: This is only settable when creating the control since once the user sorts things we don't have code to put the humpty dumpty back together again.//
|
||
|
|
inViewSorting = ((Boolean) viewMessage.getMessageData()).booleanValue();
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SHOW_GRID_LINES: {
|
||
|
|
getSwtTree().setLinesVisible(((Boolean) viewMessage.getMessageData()).booleanValue());
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SHOW_HEADERS: {
|
||
|
|
getSwtTree().setHeaderVisible(((Boolean) viewMessage.getMessageData()).booleanValue());
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_HEADER_RESIZEABLE: {
|
||
|
|
int columnNumber = ((Integer) ((Object[]) viewMessage.getMessageData())[0]).intValue();
|
||
|
|
boolean isResizable = ((Boolean) ((Object[]) viewMessage.getMessageData())[1]).booleanValue();
|
||
|
|
|
||
|
|
getSwtTree().getColumn(columnNumber).setResizable(isResizable);
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_COLUMN_ALIGNMENT: {
|
||
|
|
int columnNumber = ((int[]) viewMessage.getMessageData())[0];
|
||
|
|
int alignment = ((int[]) viewMessage.getMessageData())[1];
|
||
|
|
|
||
|
|
getSwtTree().getColumn(columnNumber).setAlignment(alignment == ALIGNMENT_RIGHT ? SWT.RIGHT : alignment == ALIGNMENT_CENTER ? SWT.CENTER : SWT.LEFT);
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_COLUMN_WIDTH: {
|
||
|
|
int columnNumber = ((int[]) viewMessage.getMessageData())[0];
|
||
|
|
int width = ((int[]) viewMessage.getMessageData())[1];
|
||
|
|
|
||
|
|
getSwtTree().getColumn(columnNumber).setWidth(width);
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_COLUMN_MINIMUM_WIDTH: {
|
||
|
|
int columnNumber = viewMessage.getMessageInteger();
|
||
|
|
int minimumWidth = viewMessage.getMessageSecondaryInteger();
|
||
|
|
|
||
|
|
getColumn(columnNumber).setMinimumWidth(minimumWidth);
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_COLUMN_TOOL_TIP: {
|
||
|
|
int columnNumber = viewMessage.getMessageInteger();
|
||
|
|
Object toolTip = viewMessage.getMessageData();
|
||
|
|
|
||
|
|
getColumn(columnNumber).setToolTipText(toolTip);
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_COLUMN_MOVEABLE: {
|
||
|
|
int columnNumber = ((Integer) ((Object[]) viewMessage.getMessageData())[0]).intValue();
|
||
|
|
boolean moveable = ((Boolean) ((Object[]) viewMessage.getMessageData())[1]).booleanValue();
|
||
|
|
|
||
|
|
getSwtTree().getColumn(columnNumber).setMoveable(moveable);
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SHOW_SELECTION: {
|
||
|
|
getSwtTree().showSelection();
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_NODE_OPENED: {
|
||
|
|
NodeData node = getNodeData(viewMessage.getMessageInteger());
|
||
|
|
NodeData waitingNode = (NodeData) waitingItem.getData();
|
||
|
|
TreeItem topItem;
|
||
|
|
|
||
|
|
if(waitingNode == node) {
|
||
|
|
if(node.getChildren() == null) {
|
||
|
|
node.setChildren(LiteList.EMPTY_LIST);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//replaceTreeItemChildren(waitingItem, node);
|
||
|
|
waitingItem.setExpanded(true);
|
||
|
|
waitingItem = null;
|
||
|
|
getSwtTree().setEnabled(true);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
updateTreeEditors(false);
|
||
|
|
layoutRenderers();
|
||
|
|
|
||
|
|
//TODO: The following work around code should be replaceable by calling the layoutRenderers() method on all ColumnData instances which should reposition all renderers.
|
||
|
|
topItem = getSwtTree().getTopItem();
|
||
|
|
//getShell()SwtUtilities.setRedraw(, false);
|
||
|
|
getSwtTree().pack();
|
||
|
|
getShell().layout(true, true);
|
||
|
|
getSwtTree().setTopItem(topItem);
|
||
|
|
SwtUtilities.setRedraw(getShell(), true);
|
||
|
|
getShell().redraw();
|
||
|
|
|
||
|
|
if((focusControl != null) && (!focusControl.isDisposed())) {
|
||
|
|
focusControl.setFocus();
|
||
|
|
}//if//
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_SELECT_ON_OPEN: {
|
||
|
|
this.selectOnOpen = ((Boolean) viewMessage.getMessageData()).booleanValue();
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_AUTO_FIT: {
|
||
|
|
setAutoFit(((Boolean) viewMessage.getMessageData()).booleanValue());
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_FILL_ON_INITIALIZE: {
|
||
|
|
setFillOnInitialize(((Boolean) viewMessage.getMessageData()).booleanValue());
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_SET_FILL_ON_RESIZE: {
|
||
|
|
setFillOnResize(((Boolean) viewMessage.getMessageData()).booleanValue());
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_FILL: {
|
||
|
|
lastControlWidth = getSwtTree().getClientArea().width;
|
||
|
|
fill();
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
case MESSAGE_FIT: {
|
||
|
|
lastControlWidth = getSwtTree().getClientArea().width;
|
||
|
|
fit();
|
||
|
|
break;
|
||
|
|
}//case//
|
||
|
|
default: {
|
||
|
|
retVal = super.internalProcessMessage(viewMessage);
|
||
|
|
}//default//
|
||
|
|
}//switch//
|
||
|
|
|
||
|
|
return retVal;
|
||
|
|
}//internalProcessMessage()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlRemoveAll()
|
||
|
|
*/
|
||
|
|
protected void controlRemoveAll() {
|
||
|
|
IIterator iterator = getRowObjects();
|
||
|
|
|
||
|
|
//Release all cell component data and mappings.//
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
SimpleNodeData nodeData = (SimpleNodeData) iterator.next();
|
||
|
|
|
||
|
|
nodeData.unregisterCellComponents();
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
getSwtTree().removeAll();
|
||
|
|
}//controlRemoveAll()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlAddNodeLink(com.foundation.tcv.swt.client.TreeComponent.NodeData, com.foundation.tcv.swt.client.TreeComponent.NodeData, int[])
|
||
|
|
*/
|
||
|
|
protected void controlAddNodeLink(NodeData parent, NodeData child, int[] columnIndexMap) {
|
||
|
|
suspendResizeEvents = true;
|
||
|
|
|
||
|
|
try {
|
||
|
|
if(parent != null) {
|
||
|
|
IList displayNodes = parent.getDisplayNodes();
|
||
|
|
|
||
|
|
if((parent.getChildren() == null) || (!parent.getChildren().isChangeable())) {
|
||
|
|
parent.setChildren(new LiteList(50, 500));
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//Add the child node data to the parent's children collection.//
|
||
|
|
parent.getChildren().add(child);
|
||
|
|
//Increment the parent count for the child so we know when it can be GC'd.//
|
||
|
|
child.incrementParentCount();
|
||
|
|
|
||
|
|
//Setup the display item for the child under all visible instances of the parent data.//
|
||
|
|
for(int index = 0, length = displayNodes.getSize(); index < length; index++) {
|
||
|
|
TreeItem parentItem = (TreeItem) displayNodes.get(index);
|
||
|
|
TreeItem childItem = null;
|
||
|
|
TreeItem removedItem = null;
|
||
|
|
|
||
|
|
if((parentItem.getItemCount() == 1) && (parentItem.getItem(0).getData() == null)) {
|
||
|
|
removedItem = parentItem.getItem(0);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
if((removedItem == null) && (inViewSorting) && (lastSortedColumnIndex != -1) && (parentItem.getItemCount() > 0)) {
|
||
|
|
TreeItem[] children = parentItem.getItems();
|
||
|
|
String newText = (String) ((SimpleTreeTableCellData) child.getData()[lastSortedColumnIndex + 1]).getText();
|
||
|
|
|
||
|
|
for(int childIndex = 0; (childItem == null) && (childIndex < children.length); childIndex++) {
|
||
|
|
String nextText = children[childIndex].getText(lastSortedColumnIndex);
|
||
|
|
|
||
|
|
if(!requiresSwap(newText, nextText, isSortReversed)) {
|
||
|
|
childItem = new TreeItem(parentItem, 0, childIndex);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Add the child to the end.//
|
||
|
|
if(childItem == null) {
|
||
|
|
childItem = new TreeItem(parentItem, 0);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
childItem = new TreeItem(parentItem, 0);
|
||
|
|
//Force the parent item to expand since this is the first child and the parent must have been previously opened since it is being notified of the child.//
|
||
|
|
// parentItem.setExpanded(true);
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
if(removedItem != null) {
|
||
|
|
removedItem.dispose();
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
initializeChildItem(childItem, (SimpleNodeData) child, columnIndexMap);
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
else { //This will be run if the parent is the root node.//
|
||
|
|
TreeItem childItem = null;
|
||
|
|
|
||
|
|
if((inViewSorting) && (lastSortedColumnIndex != -1) && (getSwtTree().getItemCount() > 0)) {
|
||
|
|
TreeItem[] children = getSwtTree().getItems();
|
||
|
|
String newText = (String) ((SimpleTreeTableCellData) child.getData()[lastSortedColumnIndex + 1]).getText();
|
||
|
|
|
||
|
|
for(int childIndex = 0; childIndex < children.length; childIndex++) {
|
||
|
|
String nextText = children[childIndex].getText(lastSortedColumnIndex);
|
||
|
|
|
||
|
|
if(!requiresSwap(newText, nextText, isSortReversed)) {
|
||
|
|
childItem = new TreeItem(getSwtTree(), 0, childIndex);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Add the child to the end.//
|
||
|
|
if(childItem == null) {
|
||
|
|
childItem = new TreeItem(getSwtTree(), 0);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
childItem = new TreeItem(getSwtTree(), 0);
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
//Increment the parent count for the child so we know when it can be GC'd.//
|
||
|
|
child.incrementParentCount();
|
||
|
|
initializeChildItem(childItem, (SimpleNodeData) child, columnIndexMap);
|
||
|
|
}//else//
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
suspendResizeEvents = false;
|
||
|
|
}//finally//
|
||
|
|
|
||
|
|
updateTreeEditors();
|
||
|
|
layoutRenderers();
|
||
|
|
}//controlAddNodeLink()//
|
||
|
|
/**
|
||
|
|
* Initializes the child item with the necessary data components.
|
||
|
|
* @param childItem The child display item to be initialized.
|
||
|
|
* @param child The child data node from which the item will be initialized.
|
||
|
|
* @param columnIndexMap The map of client side column indices indexed by the server side column index.
|
||
|
|
*/
|
||
|
|
protected void initializeChildItem(TreeItem childItem, SimpleNodeData child, int[] columnIndexMap) {
|
||
|
|
SimpleTreeTableCellData cellData = (SimpleTreeTableCellData) child.getData()[0];
|
||
|
|
|
||
|
|
rowBackgroundColorHolder.setValue(child, cellData.getBackground(), true);
|
||
|
|
childItem.setBackground(createColor((JefColor) rowBackgroundColorHolder.getValue(child)));
|
||
|
|
rowForegroundColorHolder.setValue(child, cellData.getForeground(), true);
|
||
|
|
childItem.setForeground(createColor((JefColor) rowForegroundColorHolder.getValue(child)));
|
||
|
|
rowFontHolder.setValue(child, cellData.getFont(), true);
|
||
|
|
childItem.setFont(createFont((JefFont[]) rowFontHolder.getValue(child)));
|
||
|
|
|
||
|
|
for(int columnIndex = 1; columnIndex < child.getData().length; columnIndex++) {
|
||
|
|
int clientColumnIndex = columnIndexMap[columnIndex - 1];
|
||
|
|
ColumnData columnData = (ColumnData) getSwtTree().getColumn(clientColumnIndex).getData();
|
||
|
|
|
||
|
|
cellData = (SimpleTreeTableCellData) child.getData()[columnIndex];
|
||
|
|
childItem.setText(clientColumnIndex, cellData.getText() == null ? "" : (String) cellData.getText());
|
||
|
|
columnData.cellImageHolder.setValue(child, cellData.getImage(), true);
|
||
|
|
childItem.setImage(clientColumnIndex, createImage((JefImage) columnData.cellImageHolder.getValue(child)));
|
||
|
|
columnData.backgroundColorHolder.setValue(child, cellData.getBackground(), true);
|
||
|
|
childItem.setBackground(clientColumnIndex, createColor((JefColor) columnData.backgroundColorHolder.getValue(child)));
|
||
|
|
columnData.foregroundColorHolder.setValue(child, cellData.getForeground(), true);
|
||
|
|
childItem.setForeground(clientColumnIndex, createColor((JefColor) columnData.foregroundColorHolder.getValue(child)));
|
||
|
|
columnData.fontHolder.setValue(child, cellData.getFont(), true);
|
||
|
|
childItem.setFont(clientColumnIndex, createFont((JefFont[]) columnData.fontHolder.getValue(child)));
|
||
|
|
|
||
|
|
if(cellData.getCellComponentId() != -1) {
|
||
|
|
//Sets the mapping between the column/row and the cell component and it registers the row data with the cell component.//
|
||
|
|
child.registerCellComponent(columnData, cellData.getCellComponentId());
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//TODO: if it is known that a node has no child associations then the server should pass that info to the client which should use the EMPTY_LIST identifier defined by LiteList.
|
||
|
|
//If there may be children then create a dummy child which will be replaced upon opening the tree node.//
|
||
|
|
if((((SimpleNodeData) child).canHaveChildren()) && ((child.getChildren() == null) || (child.getChildren().getSize() > 0))) {
|
||
|
|
TreeItem dummyChild = new TreeItem(childItem, 0);
|
||
|
|
|
||
|
|
dummyChild.setText("...");
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
childItem.setData(child);
|
||
|
|
child.getDisplayNodes().add(childItem);
|
||
|
|
}//initializeChildItem()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlRemoveNodeLink(com.foundation.tcv.swt.client.TreeComponent.NodeData, com.foundation.tcv.swt.client.TreeComponent.NodeData)
|
||
|
|
*/
|
||
|
|
protected void controlRemoveNodeLink(NodeData parent, NodeData child) {
|
||
|
|
if(parent != null) {
|
||
|
|
IList displayNodes = parent.getDisplayNodes();
|
||
|
|
|
||
|
|
parent.getChildren().remove(child);
|
||
|
|
child.decrementParentCount();
|
||
|
|
//Note: Calling controlRemoveNode(child) is not necessary since the server will send a message when the node data is to be removed (in the TreeNode.release() method).//
|
||
|
|
|
||
|
|
//Iterate over the parent nodes to remove any children.//
|
||
|
|
for(int parentIndex = 0; parentIndex < displayNodes.getSize(); parentIndex++) {
|
||
|
|
TreeItem parentItem = (TreeItem) displayNodes.get(parentIndex);
|
||
|
|
|
||
|
|
//The parent could be disposed already in a recursive scenario.//
|
||
|
|
if(!parentItem.isDisposed()) {
|
||
|
|
TreeItem[] childItems = parentItem.getItems();
|
||
|
|
boolean found = false;
|
||
|
|
|
||
|
|
//Search for the correct child and dispose of it (which also disposes of all its children).//
|
||
|
|
if(childItems != null) {
|
||
|
|
for(int childIndex = 0; (!found) && (childIndex < childItems.length); childIndex++) {
|
||
|
|
TreeItem childItem = childItems[childIndex];
|
||
|
|
|
||
|
|
//If this is the correct child item then remove all it's children's linkages (recursively) and dispose of it and its children.//
|
||
|
|
if(childItem.getData() == child) {
|
||
|
|
found = true;
|
||
|
|
removeNodeLinkages(childItem);
|
||
|
|
childItem.dispose();
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
//Ensure that the tree is not disposed already.//
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
TreeItem[] childItems = getSwtTree().getItems();
|
||
|
|
boolean found = false;
|
||
|
|
|
||
|
|
//Search for the correct child and dispose of it (which also disposes of all its children).//
|
||
|
|
if(childItems != null) {
|
||
|
|
for(int childIndex = 0; (!found) && (childIndex < childItems.length); childIndex++) {
|
||
|
|
TreeItem childItem = childItems[childIndex];
|
||
|
|
|
||
|
|
//If this is the correct child item then remove all it's children's linkages (recursively) and dispose of it and its children.//
|
||
|
|
if(childItem.getData() == child) {
|
||
|
|
found = true;
|
||
|
|
removeNodeLinkages(childItem);
|
||
|
|
childItem.dispose();
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
updateTreeEditors();
|
||
|
|
layoutRenderers();
|
||
|
|
}//controlRemoveNodeLink()//
|
||
|
|
/**
|
||
|
|
* Removes the linkages for the item's children in a recursive fashion.
|
||
|
|
* @param item The item whose children are to be unlinked to their NodeData instances.
|
||
|
|
*/
|
||
|
|
private void removeNodeLinkages(TreeItem item) {
|
||
|
|
if(item.getItemCount() > 0) {
|
||
|
|
TreeItem[] items = item.getItems();
|
||
|
|
|
||
|
|
for(int index = 0; index < items.length; index++) {
|
||
|
|
NodeData nodeData = (NodeData) items[index].getData();
|
||
|
|
|
||
|
|
if(nodeData != null) {
|
||
|
|
nodeData.getDisplayNodes().remove(items[index]);
|
||
|
|
removeNodeLinkages(items[index]);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//removeNodeLinkages()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#createServerToClientColumnMapping()
|
||
|
|
*/
|
||
|
|
protected int[] createServerToClientColumnMapping() {
|
||
|
|
int[] clientToServerColumnMapping = getSwtTree().getColumnOrder();
|
||
|
|
int[] result = new int[clientToServerColumnMapping.length];
|
||
|
|
|
||
|
|
for(int index = 0; index < result.length; index++) {
|
||
|
|
result[clientToServerColumnMapping[index]] = index;
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//createServerToClientColumnMapping()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#createClientToServerColumnMapping()
|
||
|
|
*/
|
||
|
|
protected int[] createClientToServerColumnMapping() {
|
||
|
|
return getSwtTree().getColumnOrder();
|
||
|
|
}//createClientToServerColumnMapping()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.CollectionComponent#createRowObject(int)
|
||
|
|
*/
|
||
|
|
protected RowObject createRowObject(int objectId) {
|
||
|
|
return new SimpleNodeData(objectId, getHiddenDataCount());
|
||
|
|
}//createRowObject()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlSetCellText(com.foundation.tcv.swt.client.TreeComponent.NodeData, int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetCellText(NodeData nodeData, int columnIndex, Object data) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
IList treeItems = nodeData.getDisplayNodes();
|
||
|
|
|
||
|
|
((SimpleTreeTableCellData) nodeData.getData()[columnIndex + 1]).setText(data);
|
||
|
|
|
||
|
|
for(int index = 0; index < treeItems.getSize(); index++) {
|
||
|
|
TreeItem treeItem = (TreeItem) treeItems.get(index);
|
||
|
|
|
||
|
|
if(columnIndex != -1) {
|
||
|
|
treeItem.setText(columnIndex, data == null ? "" : (String) data);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//controlSetCellText()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlSetCellImage(com.foundation.tcv.swt.client.TreeComponent.NodeData, int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetCellImage(NodeData nodeData, int columnIndex, Object data) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
((SimpleTreeTableCellData) nodeData.getData()[columnIndex + 1]).setImage(data);
|
||
|
|
|
||
|
|
if(columnIndex != -1) {
|
||
|
|
ColumnData columnData = (ColumnData) getSwtTree().getColumn(columnIndex).getData();
|
||
|
|
|
||
|
|
columnData.cellImageHolder.setValue(nodeData, data);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//controlSetCellImage()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlSetCellBackgroundColor(com.foundation.tcv.swt.client.TreeComponent.NodeData, int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetCellBackgroundColor(NodeData nodeData, int columnIndex, Object data) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
((SimpleTreeTableCellData) nodeData.getData()[columnIndex + 1]).setImage(data);
|
||
|
|
|
||
|
|
if(columnIndex != -1) {
|
||
|
|
ColumnData columnData = (ColumnData) getSwtTree().getColumn(columnIndex).getData();
|
||
|
|
|
||
|
|
columnData.backgroundColorHolder.setValue(nodeData, data);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
rowBackgroundColorHolder.setValue(nodeData, data);
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
}//controlSetCellBackgroundColor()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlSetCellForegroundColor(com.foundation.tcv.swt.client.TreeComponent.NodeData, int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetCellForegroundColor(NodeData nodeData, int columnIndex, Object data) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
((SimpleTreeTableCellData) nodeData.getData()[columnIndex + 1]).setImage(data);
|
||
|
|
|
||
|
|
if(columnIndex != -1) {
|
||
|
|
ColumnData columnData = (ColumnData) getSwtTree().getColumn(columnIndex).getData();
|
||
|
|
|
||
|
|
columnData.foregroundColorHolder.setValue(nodeData, data);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
rowForegroundColorHolder.setValue(nodeData, data);
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
}//controlSetCellForegroundColor()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlSetCellFont(com.foundation.tcv.swt.client.TreeComponent.NodeData, int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetCellFont(NodeData nodeData, int columnIndex, Object data) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
((SimpleTreeTableCellData) nodeData.getData()[columnIndex + 1]).setImage(data);
|
||
|
|
|
||
|
|
if(columnIndex != -1) {
|
||
|
|
ColumnData columnData = (ColumnData) getSwtTree().getColumn(columnIndex).getData();
|
||
|
|
|
||
|
|
columnData.fontHolder.setValue(nodeData, data);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
rowFontHolder.setValue(nodeData, data);
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
}//controlSetCellFont()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlSetColumnHeaderText(int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetColumnHeaderText(int columnIndex, Object text) {
|
||
|
|
ColumnData columnData = (ColumnData) getSwtTree().getColumn(columnIndex).getData();
|
||
|
|
|
||
|
|
columnData.headerTextHolder.setValue(text);
|
||
|
|
}//controlSetColumnHeaderText()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlSetColumnHeaderImage(int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetColumnHeaderImage(int columnIndex, Object image) {
|
||
|
|
ColumnData columnData = (ColumnData) getSwtTree().getColumn(columnIndex).getData();
|
||
|
|
|
||
|
|
columnData.headerImageHolder.setValue(image);
|
||
|
|
}//controlSetColumnHeaderImage()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlSaveViewState()
|
||
|
|
*/
|
||
|
|
protected void controlSaveViewState() {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
TreeItem[] items = getSwtTree().getItems();
|
||
|
|
|
||
|
|
stateData = new StateData();
|
||
|
|
|
||
|
|
//Build the array of identifiers that is the path to the top item.//
|
||
|
|
if(getSwtTree().getTopItem() != null) {
|
||
|
|
int arraySize = 0;
|
||
|
|
int index = 0;
|
||
|
|
TreeItem item = getSwtTree().getTopItem();
|
||
|
|
String[] topItemPath = null;
|
||
|
|
|
||
|
|
while(item != null) {
|
||
|
|
item = item.getParentItem();
|
||
|
|
arraySize++;
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
topItemPath = new String[arraySize];
|
||
|
|
item = getSwtTree().getTopItem();
|
||
|
|
|
||
|
|
while(item != null) {
|
||
|
|
topItemPath[index++] = item.getText();
|
||
|
|
item = item.getParentItem();
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
stateData.setTopItemPath(topItemPath);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
for(int index = 0; index < items.length; index++) {
|
||
|
|
if(items[index].getExpanded()) {
|
||
|
|
StateExpansionData data = new StateExpansionData(items[index].getText());
|
||
|
|
|
||
|
|
stateData.addNode(data);
|
||
|
|
StateData.collectChildExpansionData(data, items[index]);
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
}//controlSaveViewState()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TreeComponent#controlRestoreViewState()
|
||
|
|
*/
|
||
|
|
protected void controlRestoreViewState() {
|
||
|
|
if((!getSwtTree().isDisposed()) && (stateData != null)) {
|
||
|
|
StateData.expandItems(getSwtTree().getItems(), stateData.getNodes());
|
||
|
|
|
||
|
|
if(stateData.getTopItemPath() != null) {
|
||
|
|
TreeItem item = StateData.findItem(getSwtTree().getItems(), stateData.getTopItemPath(), 0);
|
||
|
|
|
||
|
|
if(item != null) {
|
||
|
|
getSwtTree().setTopItem(item);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//controlRestoreViewState()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlAddColumn()
|
||
|
|
*/
|
||
|
|
protected void controlAddColumn() {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
internalColumnAdded(new TreeColumn(getSwtTree(), 0), getColumnCount());
|
||
|
|
}//if//
|
||
|
|
}//controlAddColumn()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlAddColumn(int)
|
||
|
|
*/
|
||
|
|
protected void controlAddColumn(int columnIndex) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
internalColumnAdded(new TreeColumn(getSwtTree(), 0, columnIndex), columnIndex);
|
||
|
|
}//if//
|
||
|
|
}//controlAddColumn()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlRemoveColumn(int)
|
||
|
|
*/
|
||
|
|
protected void controlRemoveColumn(int columnIndex) {
|
||
|
|
if(!getSwtTree().getColumn(columnIndex).isDisposed()) {
|
||
|
|
getSwtTree().getColumn(columnIndex).dispose();
|
||
|
|
}//if//
|
||
|
|
}//controlRemoveColumn()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlSetColumnHeaderData(int, java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetColumnHeaderData(int columnIndex, Object data) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
getSwtTree().getColumn(columnIndex).setText(data == null ? "" : data.toString());
|
||
|
|
}//if//
|
||
|
|
}//controlSetColumnHeaderData()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlAddSelection(java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlAddSelection(Object treeItemData) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
TreeItem treeItem = (TreeItem) treeItemData;
|
||
|
|
TreeItem[] newSelections;
|
||
|
|
|
||
|
|
if(getSwtTree().getSelectionCount() > 0) {
|
||
|
|
TreeItem[] oldSelections = getSwtTree().getSelection();
|
||
|
|
|
||
|
|
newSelections = new TreeItem[oldSelections.length];
|
||
|
|
System.arraycopy(oldSelections, 0, newSelections, 0, oldSelections.length);
|
||
|
|
newSelections[oldSelections.length] = treeItem;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
newSelections = new TreeItem[] {treeItem};
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
getSwtTree().setSelection(newSelections);
|
||
|
|
|
||
|
|
if(newSelections.length == 1) {
|
||
|
|
getSwtTree().showItem(newSelections[0]);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//controlAddSelection()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlRemoveSelection(java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlRemoveSelection(Object treeItemData) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
TreeItem treeItem = (TreeItem) treeItemData;
|
||
|
|
|
||
|
|
if(getSwtTree().getSelectionCount() > 0) {
|
||
|
|
TreeItem[] oldSelections = getSwtTree().getSelection();
|
||
|
|
int treeItemIndex = -1;
|
||
|
|
|
||
|
|
for(int index = 0; (treeItemIndex == -1) && (index < oldSelections.length); index++) {
|
||
|
|
if(oldSelections[index] == treeItem) {
|
||
|
|
treeItemIndex = index;
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
if(treeItemIndex != -1) {
|
||
|
|
TreeItem[] newSelections = new TreeItem[oldSelections.length];
|
||
|
|
|
||
|
|
System.arraycopy(oldSelections, 0, newSelections, 0, treeItemIndex);
|
||
|
|
System.arraycopy(oldSelections, treeItemIndex + 1, newSelections, treeItemIndex, newSelections.length - treeItemIndex);
|
||
|
|
getSwtTree().setSelection(newSelections);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//getSwtTree().deselect(getSwtTree().indexOf(treeItem));
|
||
|
|
}//if//
|
||
|
|
}//controlRemoveSelection()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlRemoveAllSelections()
|
||
|
|
*/
|
||
|
|
protected void controlRemoveAllSelections() {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
getSwtTree().deselectAll();
|
||
|
|
}//if//
|
||
|
|
}//controlRemoveAllSelections()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlSetSelection(java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected void controlSetSelection(Object treeItemData) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
TreeItem treeItem = (TreeItem) treeItemData;
|
||
|
|
|
||
|
|
getSwtTree().setSelection(new TreeItem[] {treeItem});
|
||
|
|
getSwtTree().showItem(treeItem);
|
||
|
|
}//if//
|
||
|
|
}//controlSetSelection()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlSetSelections(com.common.util.IList)
|
||
|
|
*/
|
||
|
|
protected void controlSetSelections(IList treeItemData) {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
TreeItem[] treeItems = new TreeItem[treeItemData.getSize()];
|
||
|
|
|
||
|
|
//Collect the table items for the selected rows.//
|
||
|
|
for(int index = 0; index < treeItems.length; index++) {
|
||
|
|
treeItems[index] = (TreeItem) treeItemData.get(index);
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
getSwtTree().setSelection(treeItems);
|
||
|
|
|
||
|
|
if(treeItems.length > 0) {
|
||
|
|
getSwtTree().showItem(treeItems[0]);
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//controlSetSelections()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlGetSelection()
|
||
|
|
*/
|
||
|
|
protected int controlGetSelection() {
|
||
|
|
return getSwtTree().getSelectionCount() == 1 ? ((NodeData) getSwtTree().getSelection()[0].getData()).getObjectId() : -1;
|
||
|
|
}//controlGetSelection()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlGetSelections()
|
||
|
|
*/
|
||
|
|
protected int[] controlGetSelections() {
|
||
|
|
int[] results = null;
|
||
|
|
|
||
|
|
if(getSwtTree().getSelectionCount() > 0) {
|
||
|
|
TreeItem[] treeItems = getSwtTree().getSelection();
|
||
|
|
|
||
|
|
results = new int[treeItems.length];
|
||
|
|
|
||
|
|
for(int index = 0; index < treeItems.length; index++) {
|
||
|
|
results[index] = ((NodeData) treeItems[index].getData()).getObjectId();
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return results;
|
||
|
|
}//controlGetSelections()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlIsSelected(java.lang.Object)
|
||
|
|
*/
|
||
|
|
protected boolean controlIsSelected(Object treeItemData) {
|
||
|
|
TreeItem treeItem = (TreeItem) treeItemData;
|
||
|
|
boolean result = false;
|
||
|
|
|
||
|
|
if(getSwtTree().getSelectionCount() > 0) {
|
||
|
|
TreeItem[] oldSelections = getSwtTree().getSelection();
|
||
|
|
|
||
|
|
for(int index = 0; (!result) && (index < oldSelections.length); index++) {
|
||
|
|
result = oldSelections[index] == treeItem;
|
||
|
|
}//for//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
//return getSwtTree().isSelected(getSwtTree().indexOf(treeItem));
|
||
|
|
}//controlIsSelected()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#controlGetObjectIds()
|
||
|
|
*/
|
||
|
|
protected int[] controlGetObjectIds() {
|
||
|
|
TreeItem[] treeItems = getSwtTree().getItems();
|
||
|
|
int[] result = new int[treeItems.length];
|
||
|
|
|
||
|
|
//Collect the objectId for each row.//
|
||
|
|
for(int index = treeItems.length - 1; index >= 0; index--) {
|
||
|
|
result[index] = ((NodeData) treeItems[index].getData()).getObjectId();
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
return result;
|
||
|
|
}//controlGetObjectIds()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.CollectionComponent#controlGetSelectionCount()
|
||
|
|
*/
|
||
|
|
protected int controlGetSelectionCount() {
|
||
|
|
return getSwtTree().getSelectionCount();
|
||
|
|
}//controlGetSelectionCount()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.TableComponent#forceSelectionEvent()
|
||
|
|
*/
|
||
|
|
protected void forceSelectionEvent() {
|
||
|
|
if(!getSwtTree().isDisposed()) {
|
||
|
|
widgetSelected(null);
|
||
|
|
}//if//
|
||
|
|
}//forceSelectionEvent()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.CollectionComponent#getAutoSynchronizeSelectionDelay()
|
||
|
|
*/
|
||
|
|
protected long getAutoSynchronizeSelectionDelay() {
|
||
|
|
return disableAutoSynchronizeDelay ? 0 : super.getAutoSynchronizeSelectionDelay();
|
||
|
|
}//getAutoSynchronizeSelectionDelay()//
|
||
|
|
/**
|
||
|
|
* Gets whether resizeable columns will be called upon to fit the control's client space when the control resizes or a column resizes.
|
||
|
|
* @return Whether resizeable columns will be told to fit available space.
|
||
|
|
*/
|
||
|
|
public boolean getAutoFit() {
|
||
|
|
return autoFit;
|
||
|
|
}//getAutoFill()//
|
||
|
|
/**
|
||
|
|
* Sets whether resizeable columns will be called upon to fit the control's client space when the control resizes or a column resizes.
|
||
|
|
* @param autoFit Whether resizeable columns will be told to fit available space.
|
||
|
|
*/
|
||
|
|
public void setAutoFit(boolean autoFit) {
|
||
|
|
this.autoFit = autoFit;
|
||
|
|
}//setAutoFit()//
|
||
|
|
/**
|
||
|
|
* Gets whether resizeable columns will be streched to fill available space during the control's initialization.
|
||
|
|
* @return Whether resizeable columns will be expanded to fill in empty space.
|
||
|
|
*/
|
||
|
|
public boolean getFillOnInitialize() {
|
||
|
|
return fillOnInitialize;
|
||
|
|
}//getFillOnInitialize()//
|
||
|
|
/**
|
||
|
|
* Sets whether resizeable columns will be streched to fill available space during the control's initialization.
|
||
|
|
* @param fillOnInitialize Whether resizeable columns will be expanded to fill in empty space.
|
||
|
|
*/
|
||
|
|
public void setFillOnInitialize(boolean fillOnInitialize) {
|
||
|
|
this.fillOnInitialize = fillOnInitialize;
|
||
|
|
}//setFillOnInitialize()//
|
||
|
|
/**
|
||
|
|
* Gets whether resizeable columns will be streched to fill available space when resizing the control.
|
||
|
|
* @return Whether resizeable columns will be expanded to fill in empty space.
|
||
|
|
*/
|
||
|
|
public boolean getFillOnResize() {
|
||
|
|
return fillOnResize;
|
||
|
|
}//getFillOnResize()//
|
||
|
|
/**
|
||
|
|
* Sets whether resizeable columns will be streched to fill available space when resizing the control.
|
||
|
|
* @param fillOnResize Whether resizeable columns will be expanded to fill in empty space.
|
||
|
|
*/
|
||
|
|
public void setFillOnResize(boolean fillOnResize) {
|
||
|
|
this.fillOnResize = fillOnResize;
|
||
|
|
}//setFillOnResize()//
|
||
|
|
/**
|
||
|
|
* Releases the tree editors for the given item.
|
||
|
|
* @param treeItem The tree item to be released.
|
||
|
|
*/
|
||
|
|
protected void releaseTreeEditors(TreeItem treeItem) {
|
||
|
|
int columnCount = getColumnCount();
|
||
|
|
|
||
|
|
//Release the editors for the row and each column.//
|
||
|
|
for(int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
|
||
|
|
TreeColumn treeColumn = getSwtTree().getColumn(columnIndex);
|
||
|
|
ColumnData columnData = (ColumnData) treeColumn.getData();
|
||
|
|
TreeEditor treeEditor = columnData.removeTreeEditor(treeItem);
|
||
|
|
|
||
|
|
if(treeEditor != null) {
|
||
|
|
CellComponent cellComponent = ((CellComponent.CellControl) treeEditor.getEditor().getData()).getCellComponent();
|
||
|
|
|
||
|
|
cellComponent.destroyControl(treeEditor.getEditor());
|
||
|
|
treeEditor.dispose();
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//releaseTreeEditors()//
|
||
|
|
/**
|
||
|
|
* Creates the tree editors for the given item.
|
||
|
|
* @param treeItem The tree item to be setup.
|
||
|
|
*/
|
||
|
|
protected void createTreeEditors(TreeItem treeItem) {
|
||
|
|
int columnCount = getColumnCount();
|
||
|
|
SimpleNodeData rowObject = (SimpleNodeData) treeItem.getData();
|
||
|
|
|
||
|
|
//Release the editors for the row and each column.//
|
||
|
|
for(int columnIndex = 0; columnIndex < columnCount; columnIndex++) {
|
||
|
|
TreeColumn treeColumn = getSwtTree().getColumn(columnIndex);
|
||
|
|
ColumnData columnData = (ColumnData) treeColumn.getData();
|
||
|
|
CellComponent cellComponent = rowObject.getCellComponent(columnData);
|
||
|
|
|
||
|
|
//If there is a cell component and it allows us to use a control for this row, then set up a new control and table editor.//
|
||
|
|
if((cellComponent != null) && (cellComponent.supportsControl()) && ((cellComponent.useControl() == CellComponent.USE_CONTROL_ALWAYS) || ((cellComponent.useControl() == CellComponent.USE_CONTROL_ON_SELECTION) && ((getSwtTree().getSelectionCount() == 1) && (getSwtTree().getSelection()[0] == treeItem))))) {
|
||
|
|
Control control = cellComponent.createCellControl(rowObject);
|
||
|
|
TreeEditor treeEditor = new TreeEditor(getSwtTree());
|
||
|
|
Point size;
|
||
|
|
|
||
|
|
treeEditor.setEditor(control, treeItem, columnIndex);
|
||
|
|
columnData.setTreeEditor(treeItem, treeEditor);
|
||
|
|
treeEditor.grabHorizontal = cellComponent.grabHorizontalSpace();
|
||
|
|
treeEditor.grabVertical = cellComponent.grabVerticalSpace();
|
||
|
|
treeEditor.horizontalAlignment = cellComponent.getHorizontalAlignment();
|
||
|
|
treeEditor.verticalAlignment = cellComponent.getVerticalAlignment();
|
||
|
|
size = treeEditor.getEditor().computeSize(SWT.DEFAULT, SWT.DEFAULT);
|
||
|
|
|
||
|
|
if(size.y > minimumTreeEditorHeight) {
|
||
|
|
minimumTreeEditorHeight = size.y;
|
||
|
|
}//if//
|
||
|
|
}//if//
|
||
|
|
}//for//
|
||
|
|
}//createTreeEditors()//
|
||
|
|
/**
|
||
|
|
* Updates the tree editors so that all visible rows have appropriate tree editors when the tree scrolls, expands, or contracts.
|
||
|
|
*/
|
||
|
|
public void updateTreeEditors() {
|
||
|
|
updateTreeEditors(false);
|
||
|
|
}//updateTreeEditors()//
|
||
|
|
/**
|
||
|
|
* Updates the tree editors so that all visible rows have appropriate tree editors when the tree scrolls, expands, or contracts.
|
||
|
|
*/
|
||
|
|
public void updateTreeEditors(boolean forceRebuild) {
|
||
|
|
if(!suspendUpdateTreeEditors) {
|
||
|
|
TreeItem item = getSwtTree().getTopItem();
|
||
|
|
IIterator iterator;
|
||
|
|
|
||
|
|
if(item != null && !item.isDisposed()) {
|
||
|
|
Rectangle clientArea = getSwtTree().getClientArea();
|
||
|
|
int visibleRowCount = (int) Math.ceil(clientArea.height / (double) getSwtTree().getItemHeight());
|
||
|
|
LiteHashSet visibleTreeItems = new LiteHashSet(visibleRowCount);
|
||
|
|
TreeItem stackItem = item;
|
||
|
|
IntArray itemIndexStack = new IntArray(40);
|
||
|
|
int currentItemIndex = 0;
|
||
|
|
boolean lastItemFound = false;
|
||
|
|
|
||
|
|
//Build the stack of indices showing the location of the children in their parents down to the top item.//
|
||
|
|
while(stackItem.getParentItem() != null) {
|
||
|
|
itemIndexStack.add(0, stackItem.getParentItem().indexOf(stackItem));
|
||
|
|
stackItem = stackItem.getParentItem();
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
//Add the index of the root level item within the tree to the stack.//
|
||
|
|
itemIndexStack.add(0, stackItem.getParent().indexOf(stackItem));
|
||
|
|
//Use the last index in the stack as the current index.//
|
||
|
|
currentItemIndex = itemIndexStack.removeLast();
|
||
|
|
|
||
|
|
//Navigate the visible tree items and add them to the set.//
|
||
|
|
while((!lastItemFound) && (visibleTreeItems.getSize() < visibleRowCount)) {
|
||
|
|
//Add the visible item to the set.//
|
||
|
|
visibleTreeItems.add(item);
|
||
|
|
|
||
|
|
if(item.getExpanded() && (item.getItemCount() > 0)) {
|
||
|
|
itemIndexStack.add(currentItemIndex);
|
||
|
|
currentItemIndex = 0;
|
||
|
|
item = item.getItem(0);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
boolean hasPeer = false;
|
||
|
|
|
||
|
|
//Get the next item peer (or search up the parent chain until a peer is found).//
|
||
|
|
while(!lastItemFound && !hasPeer) {
|
||
|
|
if(item.getParentItem() != null) {
|
||
|
|
if(item.getParentItem().getItemCount() > ++currentItemIndex) {
|
||
|
|
item = item.getParentItem().getItem(currentItemIndex);
|
||
|
|
hasPeer = true;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
item = item.getParentItem();
|
||
|
|
currentItemIndex = itemIndexStack.removeLast();
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
if(item.getParent().getItemCount() > ++currentItemIndex) {
|
||
|
|
item = item.getParent().getItem(currentItemIndex);
|
||
|
|
hasPeer = true;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
lastItemFound = true;
|
||
|
|
}//else//
|
||
|
|
}//else//
|
||
|
|
}//while//
|
||
|
|
}//else//
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
if(currentlyVisibleTreeItems != null) {
|
||
|
|
iterator = currentlyVisibleTreeItems.iterator();
|
||
|
|
|
||
|
|
if(forceRebuild) {
|
||
|
|
//Release all the editors.//
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
TreeItem next = (TreeItem) iterator.next();
|
||
|
|
|
||
|
|
releaseTreeEditors(next);
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
iterator = visibleTreeItems.iterator();
|
||
|
|
|
||
|
|
//Setup the editors.//
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
TreeItem next = (TreeItem) iterator.next();
|
||
|
|
|
||
|
|
createTreeEditors(next);
|
||
|
|
}//while//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
//Release the editors for items no longer visible.//
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
TreeItem next = (TreeItem) iterator.next();
|
||
|
|
|
||
|
|
if(!visibleTreeItems.containsValue(next)) {
|
||
|
|
releaseTreeEditors(next);
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
iterator = visibleTreeItems.iterator();
|
||
|
|
|
||
|
|
//Setup the editors for newly visible items.//
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
TreeItem next = (TreeItem) iterator.next();
|
||
|
|
|
||
|
|
if(!currentlyVisibleTreeItems.containsValue(next)) {
|
||
|
|
createTreeEditors(next);
|
||
|
|
}//if//
|
||
|
|
}//while//
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
iterator = visibleTreeItems.iterator();
|
||
|
|
|
||
|
|
//Setup the editors for newly visible items.//
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
TreeItem next = (TreeItem) iterator.next();
|
||
|
|
|
||
|
|
createTreeEditors(next);
|
||
|
|
}//while//
|
||
|
|
}//else//
|
||
|
|
|
||
|
|
currentlyVisibleTreeItems = visibleTreeItems;
|
||
|
|
}//if//
|
||
|
|
else if(currentlyVisibleTreeItems != null) {
|
||
|
|
iterator = currentlyVisibleTreeItems.iterator();
|
||
|
|
|
||
|
|
//Setup the editors for newly visible items.//
|
||
|
|
while(iterator.hasNext()) {
|
||
|
|
TreeItem next = (TreeItem) iterator.next();
|
||
|
|
|
||
|
|
releaseTreeEditors(next);
|
||
|
|
}//while//
|
||
|
|
|
||
|
|
currentlyVisibleTreeItems = null;
|
||
|
|
}//else if//
|
||
|
|
}//if//
|
||
|
|
}//updateTreeEditors()//
|
||
|
|
/**
|
||
|
|
* Forces the renderers to reposition themselves since SWT has a bug where by the renderers are not updated on tree adds, removes, or row height resizing.
|
||
|
|
*/
|
||
|
|
protected void layoutRenderers() {
|
||
|
|
// SwtUtilities.setRedraw(getSwtTree(), false);
|
||
|
|
|
||
|
|
try {
|
||
|
|
for(int columnIndex = getColumnCount() - 1; columnIndex >= 0; columnIndex--) {
|
||
|
|
((ColumnData) getColumn(columnIndex)).layoutRenderers();
|
||
|
|
}//for//
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
// SwtUtilities.setRedraw(getSwtTree(), true);
|
||
|
|
// getSwtTree().redraw();
|
||
|
|
}//finally//
|
||
|
|
}//layoutRenderers()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
|
||
|
|
*/
|
||
|
|
public void widgetDefaultSelected(SelectionEvent event) {
|
||
|
|
//Fix a bug in the table where a double click event is fired twice.//
|
||
|
|
if((event.widget == getSwtTree()) && (lastDoubleClickTime != event.time)) {
|
||
|
|
//Invoke linkages associated with the selection.//
|
||
|
|
updateSelectionLinks();
|
||
|
|
|
||
|
|
if(getSendDoubleClick()) {
|
||
|
|
//Hold the messages so they get sent together.//
|
||
|
|
addMessageHold();
|
||
|
|
//Set the synchronize delay to zero so the synchronize occurs immediatly.//
|
||
|
|
disableAutoSynchronizeDelay = true;
|
||
|
|
|
||
|
|
try {
|
||
|
|
//Synchronize the selection.//
|
||
|
|
synchronizeSelection();
|
||
|
|
|
||
|
|
//Force any selection task to complete prior to sending the double click.//
|
||
|
|
synchronized(this) {
|
||
|
|
if(autoSynchronizeSelectionTask != null) {
|
||
|
|
autoSynchronizeSelectionTask.run();
|
||
|
|
}//if//
|
||
|
|
}//synchronized//
|
||
|
|
|
||
|
|
//Send the double click message.//
|
||
|
|
sendRoundTripMessage(MESSAGE_SEND_DOUBLE_CLICK, null, null);
|
||
|
|
}//try//
|
||
|
|
finally {
|
||
|
|
//Reset the delay and send the messages.//
|
||
|
|
disableAutoSynchronizeDelay = false;
|
||
|
|
removeMessageHold();
|
||
|
|
}//finally//
|
||
|
|
|
||
|
|
lastDoubleClickTime = event.time;
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
widgetSelected(event);
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
else if(event.widget == getSwtTree().getVerticalBar()) {
|
||
|
|
updateTreeEditors();
|
||
|
|
}//else//
|
||
|
|
}//widgetDefaultSelected()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
|
||
|
|
*/
|
||
|
|
public void widgetSelected(SelectionEvent event) {
|
||
|
|
if((event == null) || (event.widget == getSwtTree())) {
|
||
|
|
//Invoke linkages associated with the selection.//
|
||
|
|
updateSelectionLinks();
|
||
|
|
//TODO: Is there some way to determine exactly what selection or deselection occured given the event object?//
|
||
|
|
synchronizeSelection();
|
||
|
|
}//if//
|
||
|
|
else if(event.widget == getSwtTree().getVerticalBar()) {
|
||
|
|
updateTreeEditors();
|
||
|
|
}//else//
|
||
|
|
}//widgetSelected()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent)
|
||
|
|
*/
|
||
|
|
public void controlMoved(ControlEvent event) {
|
||
|
|
//Ignore this.//
|
||
|
|
}//controlMoved()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent)
|
||
|
|
*/
|
||
|
|
public void controlResized(ControlEvent event) {
|
||
|
|
if(!suspendResizeEvents) {
|
||
|
|
if(getAutoFit()) {
|
||
|
|
fit();
|
||
|
|
}//if//
|
||
|
|
else if(getFillOnResize()) {
|
||
|
|
fill();
|
||
|
|
}//else if//
|
||
|
|
|
||
|
|
updateTreeEditors();
|
||
|
|
}//if//
|
||
|
|
}//controlResized()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see org.eclipse.swt.events.TreeListener#treeCollapsed(org.eclipse.swt.events.TreeEvent)
|
||
|
|
*/
|
||
|
|
public void treeCollapsed(TreeEvent event) {
|
||
|
|
updateTreeEditors();
|
||
|
|
layoutRenderers();
|
||
|
|
}//treeCollapsed()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see org.eclipse.swt.events.TreeListener#treeExpanded(org.eclipse.swt.events.TreeEvent)
|
||
|
|
*/
|
||
|
|
public void treeExpanded(TreeEvent event) {
|
||
|
|
TreeItem parentItem = (TreeItem) event.item;
|
||
|
|
|
||
|
|
focusControl = getDisplay().getFocusControl();
|
||
|
|
|
||
|
|
//If we haven't already loaded the tree items then do so now. If the parent can't have children then a dummy child won't have been created so it would be impossible for this method to be called.//
|
||
|
|
if((parentItem.getItemCount() == 1) && (parentItem.getItems()[0].getData() == null)) {
|
||
|
|
SimpleNodeData parent = (SimpleNodeData) parentItem.getData();
|
||
|
|
|
||
|
|
if(parent.getChildren() == null) {
|
||
|
|
TreeItem[] items = parentItem.getItems();
|
||
|
|
|
||
|
|
//Dispose of the old node(s).//
|
||
|
|
for(int index = 0; index < items.length; index++) {
|
||
|
|
items[index].dispose();
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Send a message to open the node which will send a reply message indicating that the node has finished opening. Disable the control while this is occuring.//
|
||
|
|
getSwtTree().setEnabled(false);
|
||
|
|
waitingItem = parentItem;
|
||
|
|
SwtUtilities.setRedraw(getShell(), false);
|
||
|
|
sendMessage(MESSAGE_OPEN_NODE, null, null, parent.getObjectId(), 0);
|
||
|
|
}//if//
|
||
|
|
else {
|
||
|
|
replaceTreeItemChildren(parentItem, parent);
|
||
|
|
updateTreeEditors();
|
||
|
|
layoutRenderers();
|
||
|
|
}//else//
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
//If the previous if block was called we could add this code to the server instead of sending two messages.//
|
||
|
|
if(getSelectOnOpen()) {
|
||
|
|
getSwtTree().setSelection(new TreeItem[] {parentItem});
|
||
|
|
synchronizeSelection();
|
||
|
|
}//if//
|
||
|
|
}//treeExpanded()//
|
||
|
|
/**
|
||
|
|
* Replaces the children TreeItems with the latest data from the parent.
|
||
|
|
* <p>Note: This method expects that the children are not linked to the node data in any way. It was designed for use when there is a temporary child that needs replacing once the node is opened.</p>
|
||
|
|
* @param parentItem The parent display item.
|
||
|
|
* @param parent The parent node data.
|
||
|
|
*/
|
||
|
|
protected void replaceTreeItemChildren(TreeItem parentItem, NodeData parent) {
|
||
|
|
TreeItem[] items = parentItem.getItems();
|
||
|
|
int[] columnMapping = createServerToClientColumnMapping();
|
||
|
|
|
||
|
|
//Dispose of the old node(s).//
|
||
|
|
for(int index = 0; index < items.length; index++) {
|
||
|
|
if(items[index].getData() != null) {
|
||
|
|
((NodeData) items[index].getData()).getDisplayNodes().remove(items[index]);
|
||
|
|
}//if//
|
||
|
|
|
||
|
|
items[index].dispose();
|
||
|
|
}//for//
|
||
|
|
|
||
|
|
//Initialize the new child display items.//
|
||
|
|
for(int index = 0; index < parent.getChildren().getSize(); index++) {
|
||
|
|
NodeData child = (NodeData) parent.getChildren().get(index);
|
||
|
|
TreeItem childItem = new TreeItem(parentItem, 0);
|
||
|
|
|
||
|
|
initializeChildItem(childItem, (SimpleNodeData) child, columnMapping);
|
||
|
|
}//for//
|
||
|
|
}//replaceTreeItemChildren()//
|
||
|
|
/**
|
||
|
|
* Denotes whether or not opening a node should trigger the selection event.
|
||
|
|
* @return boolean False by default, true if the selection event should be occuring.
|
||
|
|
*/
|
||
|
|
public boolean getSelectOnOpen() {
|
||
|
|
return this.selectOnOpen;
|
||
|
|
}//getSelectOnOpen()//
|
||
|
|
/* (non-Javadoc)
|
||
|
|
* @see com.foundation.tcv.swt.client.CollectionComponent#postChangeCollection()
|
||
|
|
*/
|
||
|
|
protected void internalPostChangeCollection() {
|
||
|
|
//Update the editors and re-layout the renderers after making changes to the data structures.//
|
||
|
|
updateTreeEditors();
|
||
|
|
layoutRenderers();
|
||
|
|
}//internalPostChangeCollection()//
|
||
|
|
}//SimpleTreeTable//
|