/* * Copyright (c) 2005,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.view.swt; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.TableEditor; 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.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.Shell; import org.eclipse.swt.widgets.Table; import org.eclipse.swt.widgets.TableColumn; import org.eclipse.swt.widgets.TableItem; import com.common.debug.Debug; import com.common.exception.InvalidArgumentException; import com.common.util.ICollection; 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.foundation.controller.DecorationManager; import com.foundation.view.*; import com.foundation.view.resource.AbstractResourceService; import com.foundation.view.resource.ResourceReference; import com.foundation.view.swt.cell.CellComponent; import com.foundation.view.swt.util.SwtUtilities; /* * What started out as a simple table control has turned out to be not so simple. This table control does almost everything one would want from a table control with possibly the exception of multi line rows. *

TODO: *

*

*/ public class SimpleTable extends TableComponent implements SelectionListener, ControlListener { public static final int STYLE_SINGLE = SWT.SINGLE; public static final int STYLE_MULTI = SWT.MULTI; public static final int STYLE_CHECK = SWT.CHECK; public static final int STYLE_FULL_SELECTION = SWT.FULL_SELECTION; public static final int STYLE_HIDE_SELECTION = SWT.HIDE_SELECTION; public static final int STYLE_VIRTUAL = SWT.VIRTUAL; public static final int ALIGNMENT_LEFT = 0; public static final int ALIGNMENT_CENTER = 1; public static final int ALIGNMENT_RIGHT = 2; public static final int AUTO_FIT_NEVER = 0; public static final int AUTO_FIT_MANUAL = 1; public static final int AUTO_FIT_AUTO = 2; public static final int LINK_TARGET_FIT = TableComponent.LAST_LINK_TARGET + 1; public static final int LINK_TARGET_FILL = TableComponent.LAST_LINK_TARGET + 2; public static final int LAST_LINK_TARGET = TableComponent.LAST_LINK_TARGET + 2; /** The events associations that resize all non-auto-fit columns to fit the size of the data in the column. */ private IHashSet fitEventAssociations = null; /** The event associations resize all auto-fill columns to fill empty space in the control. This never makes columns smaller. */ private IHashSet fillEventAssociations = null; /** 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; /** Whether the sorting is managed by the view versus the model. */ private boolean inViewSorting = true; /** A flag to temporarily disable the delay when synchronizing the selection before sending the double click message. */ private boolean disableAutoSynchronizeDelay = false; /** 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 set of currently visible table items. */ private LiteHashSet currentlyVisibleTableItems = null; /** Allows us to ignore resize events while creating items during initialization. */ private boolean suspendResizeEvents = false; /** Allows us to ignore table editor updates while adding, removing, or moving large numbers of items. */ private int suspendUpdateTableEditorsCount = 0; /** Suspends the laying out of the renderers while adding, removing, or moving large numbers of items. */ private boolean suspendLayoutRenderers = false; /** Allows us to ignore resize events for the column while resizing the columns. */ private boolean suspendColumnResizeEvents = 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 table editor height for all tested table editors. */ private int minimumTableEditorHeight = -1; /** A flag indicating whether the control's selection events should be ignored temporarily. */ protected boolean suspendSelectionEvents = false; /** * A customized row object. */ protected class SimpleTableRowObject extends TableRowObject { public SimpleTableRowObject(Object value) { super(value); }//SimpleTableRowObject()// }//SimpleTableRowObject// public static class Renderer { public CellComponent component = null; public Class type; public Renderer(Class type, CellComponent component) { if(type == null || component == null) { throw new InvalidArgumentException(); }//if// this.type = type; this.component = component; }//Renderer()// }//Renderer// /** * Encapsulates all the data pertaining to a single column in the table component. */ public class ColumnData extends AbstractColumn implements IResourceHolderComponent { /** The zero based index of this column in the set of columns. Note that this is not display order, just an identifier. */ private int index = 0; /** The text resource for the column. */ private SingleResourceAssociation headerText = new SingleResourceAssociation(this, this, getViewContext(), ResourceAssociation.TYPE_TEXT, false, ""); /** The image resource for the column. */ private SingleResourceAssociation headerImage = new SingleResourceAssociation(this, this, getViewContext(), ResourceAssociation.TYPE_IMAGE, false, null); /** The text resource for the cell. */ private MultiResourceAssociation cellText = new MultiResourceAssociation(this, this, getViewContext(), ResourceAssociation.TYPE_TEXT, false, null); /** The image resource for the cell. */ private MultiResourceAssociation cellImage = new MultiResourceAssociation(this, this, getViewContext(), ResourceAssociation.TYPE_IMAGE, false, null); /** The background color resource for the cell. */ private MultiResourceAssociation backgroundColor = new MultiResourceAssociation(this, this, getViewContext(), ResourceAssociation.TYPE_COLOR, false, null); /** The foreground color resource for the cell. */ private MultiResourceAssociation foregroundColor = new MultiResourceAssociation(this, this, getViewContext(), ResourceAssociation.TYPE_COLOR, false, null); /** The font resource for the cell. */ private MultiResourceAssociation font = new MultiResourceAssociation(this, this, getViewContext(), ResourceAssociation.TYPE_FONT, false, null); /** A holder for the value of the cell background color. */ private MultiResourceHolder backgroundColorHolder = new MultiResourceHolder(this); /** A holder for the value of the cell foreground color. */ private MultiResourceHolder foregroundColorHolder = new MultiResourceHolder(this); /** A holder for the value of the cell foreground color. */ private MultiResourceHolder fontHolder = new MultiResourceHolder(this); /** A holder for the value of the cell image. */ private MultiResourceHolder cellImageHolder = new MultiResourceHolder(this); /** A holder for the value of the column's tool tip text. */ private ResourceHolder toolTipTextHolder = new ResourceHolder(this); /** A holder for the value of the column's header text. */ private ResourceHolder headerTextHolder = new ResourceHolder(this); /** A holder for the value of the column's header image. */ private ResourceHolder headerImageHolder = new ResourceHolder(this); /** The alginment of the column data. */ private int alignment = ALIGNMENT_LEFT; /** The width of the column. */ private int width = 100; /** The minimum width of the column. */ private int minimumWidth = 20; /** Whether the column is moveable. */ private boolean moveable = true; /** The collection of Renderer instances in the order they were specified. */ private IList renderers = null; /** The set of TableEditor instances mapped by the TableItem they are serving. This is used primarily to release the editors when the are out of view. */ private LiteHashMap editorByTableItem = new LiteHashMap(50); /** A counter for the number of renderers that will paint their data. */ private int paintRendererCounter = 0; /** A counter for the number of renderers that will generate a component to display their data. */ private int componentRendererCounter = 0; /** The table column being served. */ private TableColumn column; /** * ColumnData constructor. */ public ColumnData(int index) { this.index = index; }//ColumnData()// /* (non-Javadoc) * @see com.foundation.view.IAbstractComponent#verifyThread() */ public void verifyThread() { SimpleTable.this.verifyThread(); }//verifyThread()// /** * Sets the swt table column reference. * @param column The actual column. */ public void setTableColumn(TableColumn column) { this.column = column; }//setTableColumn()// /* (non-Javadoc) * @see com.foundation.view.IAbstractComponent#getName() */ public String getName() { return "__Column__"; }//getName()// /** * Forces the renderers to layout or reposition. */ public void layoutRenderers() { IIterator iterator = editorByTableItem.valueIterator(); while(iterator.hasNext()) { ((TableEditor) iterator.next()).layout(); }//while// }//layoutRenderers()// /** * Gets the table editor associated with the given row object. * @param tableItem The table item whose editor should be retrieved. * @return The editor, or null if the row does not have an editor for this column. */ public TableEditor getTableEditor(TableItem tableItem) { return (TableEditor) editorByTableItem.get(tableItem); }//getTableEditor()// /** * Sets the table editor associated with the given row object. * @param tableItem The table item whose editor should be set. * @param tableEditor The editor, or null if the row does not have an editor for this column. */ public void setTableEditor(TableItem tableItem, TableEditor tableEditor) { editorByTableItem.put(tableItem, tableEditor); }//setTableEditor()// /** * Removes and returns the table editor associated with the given row object. * @param tableItem The table item whose editor should be removed. * @return The editor, or null if the row does not have an editor for this column. */ public TableEditor removeTableEditor(TableItem tableItem) { return (TableEditor) editorByTableItem.remove(tableItem); }//getTableEditor()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.TableComponent.AbstractColumn#registerItem(java.lang.Object) */ protected void registerItem(Object item, TableRowObject rowObject) { cellText.registerItem(item, rowObject); cellText.refresh(item); cellImage.registerItem(item, rowObject); if(cellImage.refresh(item)) { cellImageHolder.setValue(rowObject, cellImage.getValue(item), false); }//if// backgroundColor.registerItem(item, rowObject); if(backgroundColor.refresh(item)) { backgroundColorHolder.setValue(rowObject, backgroundColor.getValue(item), false); }//if// foregroundColor.registerItem(item, rowObject); if(foregroundColor.refresh(item)) { foregroundColorHolder.setValue(rowObject, foregroundColor.getValue(item), false); }//if// font.registerItem(item, rowObject); if(font.refresh(item)) { fontHolder.setValue(rowObject, font.getValue(item), false); }//if// if(renderers != null) { for(int index = 0; index < renderers.getSize(); index++) { ((Renderer) renderers.get(index)).component.registerRowItem(item, null); }//for// }//if// }//registerItem()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.TableComponent.AbstractColumn#unregisterItem(java.lang.Object) */ protected void unregisterItem(Object item) { cellText.unregisterItem(item); cellImage.unregisterItem(item); backgroundColor.unregisterItem(item); backgroundColorHolder.remove(item); foregroundColor.unregisterItem(item); foregroundColorHolder.remove(item); font.unregisterItem(item); fontHolder.remove(item); if(renderers != null) { for(int index = 0; index < renderers.getSize(); index++) { ((Renderer) renderers.get(index)).component.unregisterRowItem(item); }//for// }//if// }//unregisterItem()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.TableComponent.AbstractColumn#unregisterAllItems() */ protected void unregisterAllItems() { cellText.unregisterAllItems(); cellImage.unregisterAllItems(); backgroundColor.unregisterAllItems(); backgroundColorHolder.removeAll(); foregroundColor.unregisterAllItems(); foregroundColorHolder.removeAll(); font.unregisterAllItems(); fontHolder.removeAll(); if(renderers != null) { for(int index = 0; index < renderers.getSize(); index++) { ((Renderer) renderers.get(index)).component.unregisterRowItems(); }//for// }//if// }//unregisterAllItems()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.TableComponent.AbstractColumn#getIndex() */ protected int getIndex() { return index; }//getIndex()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.TableComponent.AbstractColumn#setIndex(int) */ protected void setIndex(int index) { this.index = index; }//setIndex()// /* (non-Javadoc) * @see com.foundation.view.IAbstractComponent#getContainer() */ public IAbstractContainer getContainer() { return SimpleTable.this.getContainer(); }//getContainer()// /* (non-Javadoc) * @see com.foundation.view.IAbstractComponent#onEventFired(com.foundation.view.IEventAssociation, java.lang.Object[]) */ public void onEventFired(IEventAssociation eventAssociation, Object[] eventArguments) { }//onEventFired()// /* (non-Javadoc) * @see com.foundation.view.IAbstractComponent#onValueChanged(com.foundation.view.IAttributeAssociation) */ public void onValueChanged(IAttributeAssociation attributeAssociation) { }//onValueChanged()// /* (non-Javadoc) * @see com.foundation.view.ISingleResourceAssociationChangeListener#addMessageHold() */ public void addMessageHold() { }//addMessageHold()// /* (non-Javadoc) * @see com.foundation.view.ISingleResourceAssociationChangeListener#removeMessageHold() */ public void removeMessageHold() { }//removeMessageHold()// /* (non-Javadoc) * @see com.foundation.view.ISingleResourceAssociationChangeListener#onValueChanged(com.foundation.view.ResourceAssociation, int) */ public void onValueChanged(ResourceAssociation resourceAssociation, int flags) { if(resourceAssociation instanceof SingleResourceAssociation) { //TODO:Remove me once we verify it isn't necessary. verifyThread(); internalOnValueChanged((SingleResourceAssociation) resourceAssociation); }//if// }//onValueChanged()// /* (non-Javadoc) * @see com.foundation.view.IMultiResourceAssociationChangeListener#onValueChanged(com.foundation.view.ResourceAssociation, java.lang.Object, boolean) */ public void onValueChanged(ResourceAssociation resourceAssociation, Object item, Object data, boolean isUpdate) { if(resourceAssociation instanceof MultiResourceAssociation) { //TODO:Remove me once we verify it isn't necessary. verifyThread(); internalOnValueChanged((MultiResourceAssociation) resourceAssociation, item, data, isUpdate); }//if// }//onValueChanged()// /* (non-Javadoc) * @see com.foundation.view.ISingleResourceAssociationChangeListener#onModelExternallyChanged(com.foundation.view.ResourceAssociation, boolean, java.lang.Object) */ public void onModelExternallyChanged(ResourceAssociation resourceAssociation, boolean isCleared, Object originalValue) { if(isInitialized()) { verifyThread(); internalOnModelExternallyChanged((SingleResourceAssociation) resourceAssociation, isCleared, originalValue); }//if// }//onModelExternallyChanged()// /* (non-Javadoc) * @see com.foundation.view.IMultiResourceAssociationChangeListener#onModelExternallyChanged(com.foundation.view.ResourceAssociation, java.lang.Object, java.lang.Object, boolean, java.lang.Object) */ public void onModelExternallyChanged(ResourceAssociation resourceAssociation, Object alteredItem, Object data, boolean isCleared, Object originalValue) { if(isInitialized()) { verifyThread(); internalOnModelExternallyChanged((MultiResourceAssociation) resourceAssociation, alteredItem, data, isCleared, originalValue); }//if// }//onModelExternallyChanged()// /* (non-Javadoc) * @see com.foundation.view.IResourceHolderComponent#resourceHolderChanged(com.foundation.view.ResourceHolder, java.lang.Object, java.lang.Object, int) */ public void resourceHolderChanged(com.foundation.view.AbstractResourceHolder resourceHolder, Object oldValue, Object newValue, int flags) { verifyThread(); internalResourceHolderChanged((ResourceHolder) resourceHolder, oldValue, newValue, flags); }//resourceHolderChanged()// /* (non-Javadoc) * @see com.foundation.view.IResourceHolderComponent#resourceHolderChanged(com.foundation.view.AbstractMultiResourceHolder, com.common.util.IHashSet, java.lang.Object, java.lang.Object) */ public void resourceHolderChanged(AbstractMultiResourceHolder resourceHolder, IHashSet rows, Object oldValue, Object newValue) { //Not used.// }//resourceHolderChanged()// /* (non-Javadoc) * @see com.foundation.view.IResourceHolderComponent#resourceHolderChanged(com.foundation.view.AbstractMultiResourceHolder, java.lang.Object, java.lang.Object, java.lang.Object) */ public void resourceHolderChanged(AbstractMultiResourceHolder resourceHolder, Object row, Object oldValue, Object newValue) { //Not used.// }//resourceHolderChanged()// /** * Called by the resource association when a resource changes value. *

The change in value will be because the resource context altered its set of current resources and the associated resources was in the set that was altered.

* @param resourceAssociation The resource association used to register with the resource. */ protected void internalOnValueChanged(SingleResourceAssociation resourceAssociation) { if(resourceAssociation == headerText) { if(headerText.refresh()) { headerTextHolder.setValue(headerText.getValue()); }//if// }//if// else if(resourceAssociation == headerImage) { if(headerImage.refresh()) { headerImageHolder.setValue(headerImage.getValue()); }//if// }//else if// }//internalOnValueChanged()// /** * Called by the resource association when a resource changes value. *

The change in value will be because the resource context altered its set of current resources and the associated resources was in the set that was altered.

* @param resourceAssociation The resource association used to register with the resource. * @param item The item whose value has been altered. * @param isUpdate Whether the change is an update from the reflected object. */ protected void internalOnValueChanged(MultiResourceAssociation resourceAssociation, Object item, Object data, boolean isUpdate) { if(isInitialized()) { if(resourceAssociation == cellText) { //Refresh the item cell data.// if(cellText.refresh(item)) { int columnIndex = getColumnIndex(); String text = (String) cellText.getValue(item); text = text == null ? "" : text; if(item != null) { SimpleTableRowObject rowObject = (SimpleTableRowObject) data; if(rowObject.controlItem == null) { for(int index = 0; index < (rowObject.controlItems != null ? rowObject.controlItems.getSize() : 0); index++) { ((TableItem) rowObject.controlItems.get(index)).setText(columnIndex, text); }//for// }//if// else { ((TableItem) rowObject.controlItem).setText(columnIndex, text); }//else// }//if// //TODO: Does this ever happen? else { IIterator iterator = getRowObjects(); while(iterator.hasNext()) { SimpleTableRowObject rowObject = (SimpleTableRowObject) iterator.next(); if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { ((TableItem) rowObject.controlItems.get(index)).setText(columnIndex, text); }//for// }//if// else { ((TableItem) rowObject.controlItem).setText(columnIndex, text); }//else// }//while// }//else// }//if// }//if// else if(resourceAssociation == cellImage) { //Refresh the item cell data.// if(cellImage.refresh(item)) { if(item != null) { cellImageHolder.setValue((SimpleTableRowObject) data, cellImage.getValue(item)); }//if// //TODO: Does this ever happen? else { Object value = cellImage.getValue(null); IIterator iterator = getRowObjects(); while(iterator.hasNext()) { cellImageHolder.setValue(iterator.next(), value); }//while// }//else// }//if// }//else if// else if(resourceAssociation == backgroundColor) { //Refresh the item cell data.// if(backgroundColor.refresh(item)) { if(item != null) { backgroundColorHolder.setValue((SimpleTableRowObject) data, backgroundColor.getValue(item)); }//if// //TODO: Does this ever happen? else { Object value = backgroundColor.getValue(null); IIterator iterator = getRowObjects(); while(iterator.hasNext()) { backgroundColorHolder.setValue(iterator.next(), value); }//while// }//else// }//if// }//else if// else if(resourceAssociation == foregroundColor) { //Refresh the item cell data.// if(foregroundColor.refresh(item)) { if(item != null) { foregroundColorHolder.setValue((SimpleTableRowObject) data, foregroundColor.getValue(item)); }//if// //TODO: Does this ever happen? else { Object value = foregroundColor.getValue(null); IIterator iterator = getRowObjects(); while(iterator.hasNext()) { foregroundColorHolder.setValue(iterator.next(), value); }//while// }//else// }//if// }//else if// else if(resourceAssociation == font) { //Refresh the item cell data.// if(font.refresh(item)) { if(item != null) { fontHolder.setValue((SimpleTableRowObject) data, font.getValue(item)); }//if// //TODO: Does this ever happen? else { Object value = font.getValue(null); IIterator iterator = getRowObjects(); while(iterator.hasNext()) { fontHolder.setValue(iterator.next(), value); }//while// }//else// }//if// }//else if// }//if// }//internalOnValueChanged()// /** * Called by the resource association (target ONLY) when the underlying model has changed, but a user's change is overriding it. * @param resourceAssociation The resource association used to register with the resource. * @param alteredItem The row item for the row being altered. * @param originalValue The original value. */ protected void internalOnModelValueChanged(ResourceAssociation resourceAssociation, Object alteredItem, Object originalValue) { }//internalOnModelValueChanged()// /** * Called by the resource association (target ONLY) when the underlying model has changed, but a user's change is overriding it. * @param resourceAssociation The resource association used to register with the resource. * @param originalValue The original value. */ protected void internalOnModelValueChanged(ResourceAssociation resourceAssociation, Object originalValue) { }//internalOnModelValueChanged()// /** * Called when a resource holder's value has been altered. * Subclasses must override this to handle change to resource holders they define. * @param resourceHolder The resource holder whose value changed. * @param oldValue The old value. * @param newValue The new value. */ protected void internalResourceHolderChanged(ResourceHolder resourceHolder, Object oldValue, Object newValue, int flags) { if(resourceHolder == toolTipTextHolder) { getSwtTable().getColumn(index).setToolTipText((String) toolTipTextHolder.getValue()); }//if// else if(resourceHolder == headerTextHolder) { column.setText((String) headerTextHolder.getValue()); }//else if// else if(resourceHolder == headerImageHolder) { if(oldValue != null) { destroyImage((JefImage) oldValue); }//if// column.setImage(createImage((JefImage) headerImageHolder.getValue())); }//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 row The row whose value has changed. * @param oldValue The old value for the resource. * @param newValue The new value for the resource. */ protected void internalResourceHolderChanged(MultiResourceHolder resourceHolder, Object row, Object oldValue, Object newValue) { int columnIndex = getColumnIndex(); if(resourceHolder == cellImageHolder) { SimpleTableRowObject rowObject = (SimpleTableRowObject) row; if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { TableItem tableItem = (TableItem) rowObject.controlItems.get(index); destroyImage(tableItem.getImage(columnIndex)); tableItem.setImage(columnIndex, createImage((JefImage) newValue)); }//for// }//if// else { TableItem tableItem = (TableItem) rowObject.controlItem; destroyImage(tableItem.getImage(columnIndex)); tableItem.setImage(columnIndex, createImage((JefImage) newValue)); }//else// }//if// else if(resourceHolder == backgroundColorHolder) { SimpleTableRowObject rowObject = (SimpleTableRowObject) row; if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { TableItem tableItem = (TableItem) rowObject.controlItems.get(index); destroyColor(tableItem.getBackground(columnIndex)); tableItem.setBackground(columnIndex, createColor((JefColor) newValue)); }//for// }//if// else { TableItem tableItem = (TableItem) rowObject.controlItem; destroyColor(tableItem.getBackground(columnIndex)); tableItem.setBackground(columnIndex, createColor((JefColor) newValue)); }//else// }//else if// else if(resourceHolder == foregroundColorHolder) { SimpleTableRowObject rowObject = (SimpleTableRowObject) row; if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { TableItem tableItem = (TableItem) rowObject.controlItems.get(index); destroyColor(tableItem.getForeground(columnIndex)); tableItem.setForeground(columnIndex, createColor((JefColor) newValue)); }//for// }//if// else { TableItem tableItem = (TableItem) rowObject.controlItem; destroyColor(tableItem.getForeground(columnIndex)); tableItem.setForeground(columnIndex, createColor((JefColor) newValue)); }//else// }//else if// else if(resourceHolder == fontHolder) { SimpleTableRowObject rowObject = (SimpleTableRowObject) row; if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { TableItem tableItem = (TableItem) rowObject.controlItems.get(index); destroyFont(tableItem.getFont(columnIndex)); tableItem.setFont(columnIndex, createFont((JefFont[]) newValue)); }//for// }//if// else { TableItem tableItem = (TableItem) rowObject.controlItem; destroyFont(tableItem.getFont(columnIndex)); tableItem.setFont(columnIndex, createFont((JefFont[]) newValue)); }//else// }//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 rows The rows whose values have changed. * @param oldValue The old value for the resource. * @param newValue The new value for the resource. */ protected void internalResourceHolderChanged(MultiResourceHolder resourceHolder, IHashSet rows, Object oldValue, Object newValue) { int columnIndex = getColumnIndex(); if(resourceHolder == cellImageHolder) { IIterator iterator = rows.iterator(); while(iterator.hasNext()) { SimpleTableRowObject rowObject = (SimpleTableRowObject) iterator.next(); if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { TableItem tableItem = (TableItem) rowObject.controlItems.get(index); destroyImage(tableItem.getImage(columnIndex)); tableItem.setImage(columnIndex, createImage((JefImage) newValue)); }//for// }//if// else { TableItem tableItem = (TableItem) rowObject.controlItem; destroyImage(tableItem.getImage(columnIndex)); tableItem.setImage(columnIndex, createImage((JefImage) newValue)); }//else// }//while// }//if// else if(resourceHolder == backgroundColorHolder) { IIterator iterator = rows.iterator(); while(iterator.hasNext()) { SimpleTableRowObject rowObject = (SimpleTableRowObject) iterator.next(); if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { TableItem tableItem = (TableItem) rowObject.controlItems.get(index); destroyColor(tableItem.getBackground(columnIndex)); tableItem.setBackground(columnIndex, createColor((JefColor) newValue)); }//for// }//if// else { TableItem tableItem = (TableItem) rowObject.controlItem; destroyColor(tableItem.getBackground(columnIndex)); tableItem.setBackground(columnIndex, createColor((JefColor) newValue)); }//else// }//while// }//else if// else if(resourceHolder == foregroundColorHolder) { IIterator iterator = rows.iterator(); while(iterator.hasNext()) { SimpleTableRowObject rowObject = (SimpleTableRowObject) iterator.next(); if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { TableItem tableItem = (TableItem) rowObject.controlItems.get(index); destroyColor(tableItem.getForeground(columnIndex)); tableItem.setForeground(columnIndex, createColor((JefColor) newValue)); }//for// }//if// else { TableItem tableItem = (TableItem) rowObject.controlItem; destroyColor(tableItem.getForeground(columnIndex)); tableItem.setForeground(columnIndex, createColor((JefColor) newValue)); }//else// }//while// }//else if// else if(resourceHolder == fontHolder) { IIterator iterator = rows.iterator(); while(iterator.hasNext()) { SimpleTableRowObject rowObject = (SimpleTableRowObject) iterator.next(); if(rowObject.controlItem == null) { for(int index = 0; index < rowObject.controlItems.getSize(); index++) { TableItem tableItem = (TableItem) rowObject.controlItems.get(index); destroyFont(tableItem.getFont(columnIndex)); tableItem.setFont(columnIndex, createFont((JefFont[]) newValue)); }//for// }//if// else { TableItem tableItem = (TableItem) rowObject.controlItem; destroyFont(tableItem.getFont(columnIndex)); tableItem.setFont(columnIndex, createFont((JefFont[]) newValue)); }//else// }//while// }//else if// else { Debug.log("Error: Unhandled resource holder change event found. ResourceHolder: " + resourceHolder); }//else// }//internalResourceHolderChanged()// /** * 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 < getSwtTable().getColumnCount()); index++) { if(getSwtTable().getColumn(index) == column) { result = index; }//if// }//for// return result; }//getColumnIndex()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.TableComponent.AbstractColumn#initialize() */ protected void initialize() { headerText.initialize(); headerImage.initialize(); cellText.initialize(); cellImage.initialize(); backgroundColor.initialize(); foregroundColor.initialize(); font.initialize(); //Force the header data to refresh since it gets set after sending the new header message to the client.// if(headerText.refresh()) { headerTextHolder.setValue(headerText.getValue()); }//if// if(headerImage.refresh()) { headerImageHolder.setValue(headerImage.getValue()); }//if// }//initialize()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent.AbstractColumn#refresh() */ protected void refresh() { }//refresh()// /* (non-Javadoc) * @see com.foundation.tcv.swt.server.TableComponent.AbstractColumn#release() */ protected void release() { unregisterAllItems(); headerText.release(); headerTextHolder.release(); headerImage.release(); headerImageHolder.release(); cellText.release(); cellImage.release(); cellImageHolder.release(); backgroundColor.release(); backgroundColorHolder.release(); foregroundColor.release(); foregroundColorHolder.release(); font.release(); fontHolder.release(); }//release()// /** * Determines whether the column header is resizable by the user. * @param headerResizable Whether the header is resizable. */ public void setHeaderResizeable(boolean headerResizable) { getSwtTable().getColumn(index).setResizable(headerResizable); }//setHeadersResizeable()// /** * Sets the columns alignment. * @param alignment The column's alignment */ public void setAlignment(int alignment) { if(this.alignment != alignment) { this.alignment = alignment; getSwtTable().getColumn(index).setAlignment(alignment == ALIGNMENT_RIGHT ? SWT.RIGHT : alignment == ALIGNMENT_CENTER ? SWT.CENTER : SWT.LEFT); }//if// }//setAlignment()// /** * Sets the column's width. * @param width The number of pixels of width for the column. */ public void setWidth(int width) { if(this.width != width) { this.width = width; getSwtTable().getColumn(index).setWidth(width); }//if// }//setWidth()// /** * 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(getSwtTable().getColumn(index).getWidth() < minimumWidth) { getSwtTable().getColumn(index).setWidth(minimumWidth); }//if// }//if// }//setMinimumWidth()// /** * Sets the column's tool tip text. * @param toolTipText The tool tip for the column. */ public void setToolTipText(String toolTipText) { toolTipTextHolder.setValue(toolTipText); }//setToolTipText()// /** * Sets the column's tool tip text. * @param toolTipText The tool tip for the column. */ public void setToolTipText(ResourceReference toolTipText) { toolTipTextHolder.setValue(toolTipText); }//setToolTipText()// /** * Sets whether the column is moveable by the user. * @param moveable Whether the column can be moved. */ public void setMoveable(boolean moveable) { if(this.moveable != moveable) { this.moveable = moveable; getSwtTable().getColumn(index).setResizable(moveable); }//if// }//setMoveable()// /** * Sets the column's default header text which is used when no other text is available. * @param headerText The column's default header text. */ public void setHeaderText(String headerText) { verifyThread(); this.headerText.setDefaultValue(headerText); }//setHeaderText()// /** * Sets the column's default header text which is used when no other text is available. * @param headerText The column's default header text. */ public void setHeaderText(ResourceReference headerText) { verifyThread(); this.headerText.setDefaultValue(headerText); }//setHeaderText()// /** * Sets the column's default header image which is used when no other image is available. * @param headerImage The column's default header image. */ public void setHeaderImage(JefImage headerImage) { verifyThread(); this.headerImage.setDefaultValue(headerImage); }//setHeaderImage()// /** * Sets the column's default header image which is used when no other image is available. * @param headerImage The column's default header image. */ public void setHeaderImage(ResourceReference headerImage) { verifyThread(); this.headerImage.setDefaultValue(headerImage); }//setHeaderImage()// /** * Sets the column's default cell text which is used when no other text is available. * @param cellText The column's default cell text. */ public void setCellText(String cellText) { verifyThread(); this.cellText.setDefaultValue(cellText); }//setCellText()// /** * Sets the column's default cell image which is used when no other image is available. * @param cellImage The column's default cell image. */ public void setCellImage(JefImage cellImage) { verifyThread(); this.cellImage.setDefaultValue(cellImage); }//setCellImage()// /** * Sets the column's default cell image which is used when no other image is available. * @param cellImage The column's default cell image. */ public void setCellImage(ResourceReference cellImage) { verifyThread(); this.cellImage.setDefaultValue(cellImage); }//setCellImage()// /** * Sets the column's default background color which is used when no other background color is available. * @param backgroundColor The column's default background color. */ public void setBackgroundColor(JefColor backgroundColor) { verifyThread(); this.backgroundColor.setDefaultValue(backgroundColor); }//setBackgroundColor()// /** * Sets the column's default background color which is used when no other background color is available. * @param backgroundColor The column's default background color. */ public void setBackgroundColor(ResourceReference backgroundColor) { verifyThread(); this.backgroundColor.setDefaultValue(backgroundColor); }//setBackgroundColor()// /** * Sets the column's default background color which is used when no other foreground color is available. * @param foregroundColor The column's default foreground color. */ public void setForegroundColor(JefColor foregroundColor) { verifyThread(); this.foregroundColor.setDefaultValue(foregroundColor); }//setForegroundColor()// /** * Sets the column's default background color which is used when no other foreground color is available. * @param foregroundColor The column's default foreground color. */ public void setForegroundColor(ResourceReference foregroundColor) { verifyThread(); this.foregroundColor.setDefaultValue(foregroundColor); }//setForegroundColor()// /** * Sets the column's default background color which is used when no other font is available. * @param font The column's default font. */ public void setFont(JefFont[] font) { verifyThread(); this.font.setDefaultValue(font); }//setFont()// /** * Sets the column's default background color which is used when no other font is available. * @param font The column's default font. */ public void setFont(ResourceReference font) { verifyThread(); this.font.setDefaultValue(font); }//setFont()// /** * Sets the association container used to access the header text. * @param container The header text association metadata. */ public void setHeaderTextAssociation(SingleAssociationContainer container) { verifyThread(); this.headerText.setAssociations(container); }//setHeaderTextAssociation()// /** * Sets the association container used to access the header image. * @param container The header image association metadata. */ public void setHeaderImageAssociation(SingleAssociationContainer container) { verifyThread(); this.headerImage.setAssociations(container); }//setHeaderImageAssociation()// /** * Sets the association container used to access the cell text. * @param container The cell text association metadata. */ public void setCellTextAssociation(MultiAssociationContainer container) { verifyThread(); this.cellText.setAssociations(container); }//setCellTextAssociation()// /** * Sets the association container used to access the cell image. * @param container The cell image association metadata. */ public void setCellImageAssociation(MultiAssociationContainer container) { verifyThread(); this.cellImage.setAssociations(container); }//setCellImageAssociation()// /** * Sets the association container used to access the background color. * @param container The background color association metadata. */ public void setBackgroundColorAssociation(MultiAssociationContainer container) { verifyThread(); this.backgroundColor.setAssociations(container); }//setBackgroundColorAssociation()// /** * Sets the association container used to access the foreground color. * @param container The foreground color association metadata. */ public void setForegroundColorAssociation(MultiAssociationContainer container) { verifyThread(); this.foregroundColor.setAssociations(container); }//setForegroundColorAssociation()// /** * Sets the association container used to access the font. * @param container The font association metadata. */ public void setFontAssociation(MultiAssociationContainer container) { verifyThread(); this.font.setAssociations(container); }//setFontAssociation()// /** * Determines whether there are renderers capable of painting their data. * @return Whether one or more renderers, that support painting a cell, were defined for this column. */ public boolean hasPaintCapableRenderers() { return paintRendererCounter != 0; }//hasPaintCapableRenderers()// /** * Determines whether there are renderers capable of creating a component. * @return Whether one or more renderers, that support creating a component, were defined for this column. */ public boolean hasComponentCapableRenderers() { return componentRendererCounter != 0; }//hasComponentCapableRenderers()// /** * Adds a cell component to the column for use with rows that match the given type name. * @param typeName The name of the class associated with the rows that will use the component to display cell data. * @param component The component that lets the user view and optionally interact with cell data. */ public void addCellComponent(Class type, CellComponent component) { if(renderers == null) { renderers = new LiteList(5, 20); }//if// renderers.add(new Renderer(type, component)); if(component.usePaint()) { paintRendererCounter++; }//if// if(component.useControl() != CellComponent.USE_CONTROL_NEVER) { componentRendererCounter++; }//if// }//addCellComponent()// /** * Gets the cell component for the given row type. * @param rowType The type of data displayed on the row for which the cell component will be used. * @return The cell component used to render and optionally interact with the cell data. This may be null in which case the default rendering will be used. */ public CellComponent getCellComponent(Class rowType) { CellComponent result = null; if(renderers != null) { for(int index = 0; (result == null) && (index < renderers.getSize()); index++) { Renderer cellDisplay = (Renderer) renderers.get(index); if((cellDisplay instanceof Renderer) && (cellDisplay.type.isAssignableFrom(rowType))) { result = ((Renderer) cellDisplay).component; }//if// }//for// }//if// return result; }//getCellComponent()// /* (non-Javadoc) * @see com.foundation.view.swt.IInternalAbstractComponent#getShell() */ public Shell getShell() { return SimpleTable.this.getSwtControl().getShell(); }//getShell()// /* (non-Javadoc) * @see com.foundation.view.IMultiResourceAssociationChangeListener#getResourceService() */ public AbstractResourceService getResourceService() { return SimpleTable.this.getResourceService(); }//getResourceService()// /* (non-Javadoc) * @see com.foundation.view.ISingleResourceAssociationChangeListener#getDecorationManager() */ public DecorationManager getDecorationManager() { return SimpleTable.this.getDecorationManager(); }//getDecorationManager()// /* (non-Javadoc) * @see com.foundation.view.ISingleResourceAssociationChangeListener#addDecoration(com.foundation.view.AbstractDecoration) */ public void addDecoration(AbstractDecoration decoration) { //Never used.// }//addDecoration()// /* (non-Javadoc) * @see com.foundation.view.ISingleResourceAssociationChangeListener#removeDecoration(com.foundation.view.AbstractDecoration) */ public void removeDecoration(AbstractDecoration decoration) { //Never used.// }//removeDecoration()// /* (non-Javadoc) * @see com.foundation.view.IMultiResourceAssociationChangeListener#addDecoration(com.foundation.view.ResourceAssociation, java.lang.Object, java.lang.Object, com.foundation.view.AbstractDecoration) */ public void addDecoration(ResourceAssociation association, Object row, Object data, AbstractDecoration decoration) { //TODO: Implement? }//addDecoration()// /* (non-Javadoc) * @see com.foundation.view.IMultiResourceAssociationChangeListener#removeDecoration(com.foundation.view.ResourceAssociation, java.lang.Object, java.lang.Object, com.foundation.view.AbstractDecoration) */ public void removeDecoration(ResourceAssociation association, Object row, Object data, AbstractDecoration decoration) { //TODO: Implement? }//removeDecoration()// }//ColumnData// /** * SimpleTable constructor. *

Not Supported Styles (Yet): STYLE_CHECK, STYLE_VIRTUAL

* @param parent The parent container for this component. * @param name The name of the component. * @param style The style of control to construct. * @see #STYLE_MULTI * @see #STYLE_SINGLE * @see #STYLE_FULL_SELECTION * @see #STYLE_HIDE_SELECTION */ public SimpleTable(Container parent, String name, int style) { super(parent, name, style); }//SimpleTable()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#initializeControl(int) */ protected void initializeControl(int style, Object data) { //Create the SWT widget.// setSwtWidget(new org.eclipse.swt.widgets.Table(((Container) 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.// getSwtTable().setHeaderVisible(true); //If the container has already been initialized then force the parent to re-layout so that this component will appear.// if(((Container) getContainer()).isInitialized()) { ((Container) getContainer()).getSwtComposite().layout(true, true); }//if// }//initializeControl()// /** * Adjusts the visible range of items to show some or all of the selections. */ public void showSelection() { getSwtTable().showSelection(); }//showSelection()// /** * Determines whether the view manages the sorting of data. *

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.

* @param inViewSorting Whether the view should handle data sorting, otherwise the model will handle it. */ public void setInViewSorting(boolean inViewSorting) { this.inViewSorting = inViewSorting; }//setInViewSorting()// /** * Determines whether to show the header. * @param showHeader Whether the header is visible. */ public void showHeaders(boolean showHeader) { getSwtTable().setHeaderVisible(showHeader); }//showHeaders()// /** * Determines whether to show the grid lines. * @param showGridLines Whether the grid lines are visible. */ public void showGridLines(boolean showGridLines) { getSwtTable().setLinesVisible(showGridLines); }//showGridLines()// /** * Gets the index of the top most visible item. * @return The top most visible item's index. */ public Integer getTopIndex() { return new Integer(getSwtTable().getTopIndex()); }//getTopIndex()// /** * Sets the index of the top most visible item. * @parma topIndex The top most visible item's index. */ public void setTopIndex(Integer topIndex) { //Tell the list to update.// getSwtTable().setTopIndex(topIndex.intValue()); }//setTopIndex()// /** * 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()// /** * Adds an association for the fit event. * @param association The association. */ public void addFitEventAssociation(IEventAssociation association) { if(fitEventAssociations == null) { fitEventAssociations = new LiteHashSet(4); }//if// association.setChangeListener(this); fitEventAssociations.add(association); if(isInitialized()) { association.register(); }//if// }//setFitEventAssociation()// /** * Adds an association for the fill event. * @param association The association. */ public void addFillEventAssociation(IEventAssociation association) { if(fillEventAssociations == null) { fillEventAssociations = new LiteHashSet(4); }//if// association.setChangeListener(this); fillEventAssociations.add(association); if(isInitialized()) { association.register(); }//if// }//setFillEventAssociation()// /** * Adds a new column to the right of the existing columns. *

Note: I have not yet allowed the removal of columns or addition of columns at specific indices.

* @return The column that was added. This can be used to setup the column's display properties. */ public ColumnData addColumn() { ColumnData result = new ColumnData(getColumnCount()); addColumn(result); return result; }//addColumn()// /* (non-Javadoc) * @see com.foundation.view.swt.CollectionComponent#itemSorted(int[]) */ protected void itemSorted(int[] mapping) { if(!inViewSorting) { super.itemSorted(mapping); }//if// }//itemSorted()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#createRowObject(java.lang.Object) */ protected TableRowObject createRowObject(Object value) { return new SimpleTableRowObject(value); }//createRowObject()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalViewInitialize() */ protected void internalViewInitialize() { getSwtTable().addSelectionListener(this); getSwtTable().addControlListener(this); getSwtTable().getVerticalBar().addSelectionListener(this); // getSwtTable().addListener(SWT.Resize, new Listener() { // public void handleEvent(Event event) { // if(getAutoFit()) { // int controlWidth = getSwtTable().getClientArea().width; // // if(controlWidth != lastControlWidth) { // lastControlWidth = controlWidth; // fit(); // }//if// // }//if// // }//handleEvent()// // }); if(fillEventAssociations != null) { IIterator iterator = fillEventAssociations.iterator(); while(iterator.hasNext()) { ((IEventAssociation) iterator.next()).register(); }//while// }//if// if(fitEventAssociations != null) { IIterator iterator = fitEventAssociations.iterator(); while(iterator.hasNext()) { ((IEventAssociation) iterator.next()).register(); }//while// }//if// super.internalViewInitialize(); if((!getAutoFit()) && (getFillOnInitialize())) { //TODO: The problem with this is the control's size is zero at this time. fill(); }//else if// }//internalViewInitialize()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#initializePaintItemListener() */ protected void initializePaintItemListener() { super.initializePaintItemListener(); getSwtTable().addListener(SWT.PaintItem, new Listener() { public void handleEvent(Event event) { SimpleTableRowObject rowObject = (SimpleTableRowObject) ((TableItem) event.item).getData(); TableColumn column = getSwtTable().getColumn(event.index); ColumnData columnData = (ColumnData) column.getData(); if(columnData.hasPaintCapableRenderers()) { CellComponent cellComponent = columnData.getCellComponent(rowObject.value.getClass()); //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.value, gc); //Reset the clipping area.// gc.setClipping(rectangle); }//if// }//if// }//handleEvent()// }); }//initializePaintItemListener()// /** * Initializes the measure item listener. */ protected void initializeMeasureItemListener() { getSwtTable().addListener(SWT.MeasureItem, new Listener() { public void handleEvent(Event event) { switch(event.type) { case SWT.MeasureItem: { if(rowHeightValue > 0) { event.height = rowHeightValue; }//if// else { TableItem item = (TableItem) event.item; //TableRowObject rowObject = (TableRowObject) item.getData(); ColumnData column = (ColumnData) getColumn(event.index); TableEditor editor = column.getTableEditor(item); if(minimumTableEditorHeight > event.height) { event.height = minimumTableEditorHeight; }//if// //Note: minimumTableEditorHeight 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// //Note: Commented out this next line because calling getItemHeight() is a recursive call.// //if(((rowHeightValue > 0) && (getSwtTable().getItemHeight() != rowHeightValue)) || (minimumTableEditorHeight > 0 && getSwtTable().getItemHeight() < minimumTableEditorHeight)) { // if(rowHeightValue > 0 || minimumTableEditorHeight > 0) { // event.height = rowHeightValue > 0 ? rowHeightValue : minimumTableEditorHeight; /* Note: Commented this block because it causes recursive redrawing in certain cases. It should also be unnecessary since the column width has nothing to do with the height. SwtUtilities.setRedraw(getSwtTable(), false); getSwtTable().getDisplay().asyncExec(new Runnable() { public void run() { try { if(getAutoFit()) { int controlWidth = getSwtTable().getClientArea().width; if(controlWidth != lastControlWidth) { lastControlWidth = controlWidth; fit(); }//if// }//if// for(int columnIndex = getColumnCount() - 1; columnIndex >= 0; columnIndex--) { ((ColumnData) getColumn(columnIndex)).layoutRenderers(); }//for// }//try// finally { SwtUtilities.setRedraw(getSwtTable(), true); getSwtTable().redraw(); }//finally// }//run()// }); */ // }//if// break; }//case// }//switch// }//handleEvent()// }); }//initializeMeasureItemListener()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalViewRelease() */ protected void internalViewRelease() { if(!getSwtTable().isDisposed()) { getSwtTable().removeSelectionListener(this); getSwtTable().removeControlListener(this); }//if// if(fillEventAssociations != null) { IIterator iterator = fillEventAssociations.iterator(); while(iterator.hasNext()) { ((IEventAssociation) iterator.next()).unregister(); }//while// }//if// if(fitEventAssociations != null) { IIterator iterator = fitEventAssociations.iterator(); while(iterator.hasNext()) { ((IEventAssociation) iterator.next()).unregister(); }//while// }//if// super.internalViewRelease(); }//internalViewRelease()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#internalViewRefresh() */ protected void internalViewRefresh() { super.internalViewRefresh(); }//internalViewRefresh()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalOnEventFired(com.foundation.view.IEventAssociation, java.lang.Object[]) */ protected void internalOnEventFired(IEventAssociation eventAssociation, Object[] eventArguments) { if((fillEventAssociations != null) && (fillEventAssociations.containsValue(eventAssociation))) { lastControlWidth = getSwtTable().getClientArea().width; fill(); }//if// else if((fitEventAssociations != null) && (fitEventAssociations.containsValue(eventAssociation))) { lastControlWidth = getSwtTable().getClientArea().width; fit(); }//else if// else { super.internalOnEventFired(eventAssociation, eventArguments); }//else// }//internalOnEventFired()// /* (non-Javadoc) * @see com.foundation.view.swt.AbstractComponent#internalOnLinkInvoked(int, java.lang.Object) */ protected void internalOnLinkInvoked(int linkTarget, Object data) { switch(linkTarget) { case LINK_TARGET_FILL: { lastControlWidth = getSwtTable().getClientArea().width; fill(); break; }//case// case LINK_TARGET_FIT: { lastControlWidth = getSwtTable().getClientArea().width; fit(); break; }//case// default: { super.internalOnLinkInvoked(linkTarget, data); break; }//default// }//switch// }//internalOnLinkInvoked()// /** * Fills the available space by expanding all resizeable columns. */ protected void fill() { int width = lastControlWidth; TableColumn[] columns = getSwtTable().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) { stopRendering(); 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; startRendering(); 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(TableColumn ignoredColumn) { int controlWidth = lastControlWidth; TableColumn[] columns = getSwtTable().getColumns(); int extraWidth = 0; LiteList resizeableColumns = new LiteList(columns.length); int totalMinimumWidth = 0; int availableWidth = controlWidth - extraWidth; int currentColumnWidthTotal = 0; int fixedWidth = 0; 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++) { TableColumn next = (TableColumn) 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++) { TableColumn next = (TableColumn) 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// stopRendering(); suspendColumnResizeEvents = true; try { for(int index = 0; index < resizeableColumns.getSize(); index++) { ((TableColumn) resizeableColumns.get(index)).setWidth(columnSizes[index]); }//for// }//try// finally { suspendColumnResizeEvents = false; startRendering(); redraw(); }//finally// }//if// else if((availableWidth < 0) && (ignoredColumn != null)) { int minimumWidth = ((ColumnData) ignoredColumn.getData()).getMinimumWidth(); stopRendering(); suspendColumnResizeEvents = true; try { //Ensure all columns that can be resized are set to their minimum width.// for(int index = 0; index < resizeableColumns.getSize(); index++) { TableColumn column = (TableColumn) 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; startRendering(); 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: " + getSwtTable().getClientArea().width + "\n"); }//fit()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#registerItem(java.lang.Object, com.foundation.view.swt.TableComponent.TableRowObject) */ protected void registerItem(Object item, TableRowObject rowObject) { super.registerItem(item, rowObject); }//registerItem()// /* (non-Javadoc) * @see com.foundation.view.swt.CollectionComponent#unregisterItem(java.lang.Object) */ protected void unregisterItem(Object item) { super.unregisterItem(item); }//unregisterItem()// /* (non-Javadoc) * @see com.foundation.view.swt.CollectionComponent#unregisterItems() */ protected void unregisterItems() { super.unregisterItems(); }//unregisterItems()// /** * Processes a double click on a table item. */ protected void doDoubleClick() { if(getDoubleClickMethod() != null) { if(getDoubleClickMethod().getIsValueHolderAssociated()) { getDoubleClickMethod().invoke(null, true); //For now we will assume no parameters. It would be nice to pass the selection perhaps.// }//if// else { Object selection = null; //Determine which selection to invoke the method on.// if(getAllowMultiSelection()) { ICollection selections = getModelSelections(); if((selections != null) && (selections.getSize() > 0)) { selection = selections.iterator().next(); }//if// }//if// else { selection = getModelSelection(); }//else// if(selection != null) { //Invoke the method on the selection object if there is one.// getDoubleClickMethod().invoke(selection, null, true); }//if// }//else// }//if// }//doDoubleClick()// /** * Called when a column is added to the table. * @param columnData The metadata for the column. This should be associated with the table column. * @param column The column added. */ protected void internalColumnAdded(ColumnData columnData, final TableColumn column) { columnData.setTableColumn(column); column.setData(columnData); //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) && (getSwtTable().getColumnCount() > 1)) { int columnCount = getSwtTable().getColumnCount(); int columnIndex = getSwtTable().indexOf(column); boolean reverse = isSortReversed = (columnIndex == lastSortedColumnIndex ? !isSortReversed : false); TableItem[] mapping = getSwtTable().getItems(); TableItem[] selected = getSwtTable().getSelection(); if(getSwtTable().getItemCount() > 0) { boolean done = false; //TODO: Replace this for large tables. //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); if(requiresSwap(text1, text2, reverse)) { TableItem temp = mapping[index]; mapping[index] = mapping[index - 1]; mapping[index - 1] = temp; done = false; }//if// text1 = text2; }//for// }//while// stopRendering(); try { //Rebuild the table items since we can't reuse them.// for(int index = 0; index < mapping.length; index++) { TableItem tableItem = (TableItem) mapping[index]; TableItem newTableItem = new TableItem(getSwtTable(), tableItem.getStyle(), index); newTableItem.setChecked(tableItem.getChecked()); newTableItem.setGrayed(tableItem.getGrayed()); newTableItem.setText(tableItem.getText()); newTableItem.setImage(tableItem.getImage()); newTableItem.setBackground(tableItem.getBackground()); newTableItem.setForeground(tableItem.getForeground()); newTableItem.setFont(tableItem.getFont()); newTableItem.setData(tableItem.getData()); for(int column = 0; column < columnCount; column++) { newTableItem.setText(column, tableItem.getText(column)); newTableItem.setImage(column, tableItem.getImage(column)); newTableItem.setBackground(column, tableItem.getBackground(column)); newTableItem.setForeground(column, tableItem.getForeground(column)); newTableItem.setFont(column, tableItem.getFont(column)); }//for// ((SimpleTableRowObject) tableItem.getData()).replaceControlItem(tableItem, newTableItem); //TODO: Replace this for large selections. for(int selectedIndex = 0; selectedIndex < selected.length; selectedIndex++) { if(selected[selectedIndex] == tableItem) { selected[selectedIndex] = newTableItem; }//if// }//for// tableItem.dispose(); }//for// updateRowColors(0); updateTableEditors(); }//try// finally { startRendering(); }//finally// }//if// //Save the last sorted column index so we know when to reverse the order of the sort.// lastSortedColumnIndex = columnIndex; //Set the new selection.// getSwtTable().setSelection(selected); //TODO: Should this be inside the try block above? getSwtTable().setSortColumn(column); getSwtTable().setSortDirection(reverse ? SWT.UP : SWT.DOWN); }//if// }//widgetSelected()// }); }//internalColumnAdded()// /** * Gets the top row index. * @return The top most visible row index. */ protected Integer internalGetTopIndex() { return new Integer(getSwtTable().getTopIndex()); }//internalGetTopIndex()// /** * Sets the top row index. * @param topIndex The top most visible row index. */ protected void internalSetTopIndex(Integer topIndex) { getSwtTable().setTopIndex(topIndex.intValue()); }//internalSetTopIndex()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlOrderRows(int[]) */ protected void controlOrderRows(int[] mapping) { if(!inViewSorting) { int columnCount = getSwtTable().getColumnCount(); TableItem[] tableItems = getSwtTable().getItems(); int tableItemCount = tableItems.length; IList newTableItems = new LiteList(tableItemCount + 100); int selectionIndex = getAllowMultiSelection() ? 0 : getSwtTable().getSelectionIndex(); int[] selectionIndices = getAllowMultiSelection() ? getSwtTable().getSelectionIndices() : null; stopRendering(); suspendSelectionEvents = true; try { for(int index = 0; index < tableItemCount; index++) { TableItem tableItem = (TableItem) tableItems[mapping[index]]; TableItem newTableItem = new TableItem(getSwtTable(), tableItem.getStyle(), index); newTableItem.setChecked(tableItem.getChecked()); newTableItem.setGrayed(tableItem.getGrayed()); newTableItem.setText(tableItem.getText()); newTableItem.setImage(tableItem.getImage()); newTableItem.setBackground(tableItem.getBackground()); newTableItem.setForeground(tableItem.getForeground()); newTableItem.setFont(tableItem.getFont()); newTableItem.setData(tableItem.getData()); for(int column = 0; column < columnCount; column++) { newTableItem.setText(column, tableItem.getText(column)); newTableItem.setImage(column, tableItem.getImage(column)); newTableItem.setBackground(column, tableItem.getBackground(column)); newTableItem.setForeground(column, tableItem.getForeground(column)); newTableItem.setFont(column, tableItem.getFont(column)); }//for// newTableItems.add(newTableItem); ((SimpleTableRowObject) tableItem.getData()).replaceControlItem(tableItem, newTableItem); tableItem.dispose(); }//for// //Refresh the selection.// if(getAllowMultiSelection()) { if(selectionIndices != null && selectionIndices.length > 0) { for(int index = 0; index < selectionIndices.length; index++) { selectionIndices[index] = mapping[selectionIndices[index]]; }//for// getSwtTable().setSelection(selectionIndices); }//if// }//if// else { if(selectionIndex >= 0) { selectionIndex = mapping[selectionIndex]; getSwtTable().setSelection(selectionIndex); }//if// }//else// }//try// finally { startRendering(); suspendSelectionEvents = false; }//finally// updateRowColors(0); }//if// }//controlOrderRows()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlRemoveAll() */ protected void controlRemoveAll() { suspendUpdateTableEditors(true); //Dispose of any color, font, or image resources used.// for(int itemIndex = 0; itemIndex < getSwtTable().getItemCount(); itemIndex++) { TableItem tableItem = getSwtTable().getItem(itemIndex); destroyImage(tableItem.getImage()); destroyColor(tableItem.getBackground()); destroyColor(tableItem.getForeground()); destroyFont(tableItem.getFont()); for(int columnIndex = 0; columnIndex < getSwtTable().getColumnCount(); columnIndex++) { destroyImage(tableItem.getImage(columnIndex)); destroyColor(tableItem.getBackground(columnIndex)); destroyColor(tableItem.getForeground(columnIndex)); destroyFont(tableItem.getFont(columnIndex)); }//for// }//for// try { getSwtTable().removeAll(); }//try// finally { suspendUpdateTableEditors(false); }//finally// updateTableEditors(); }//controlRemoveAll()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlAddRow(com.foundation.view.swt.TableComponent.TableRowObject, java.lang.Object[], int) */ protected Object controlAddRow(TableRowObject rowObject, int index) { TableItem tableItem = null; Table table = getSwtTable(); Object item = rowObject.getValue(); suspendResizeEvents = true; suspendUpdateTableEditors(true); try { int columnCount = table.getColumnCount(); JefColor rowForegroundColor = rowObject.rowForegroundColorCustomHolder != null ? (JefColor) rowObject.rowForegroundColorCustomHolder.getValue() : null; JefColor rowBackgroundColor = rowObject.rowBackgroundColorCustomHolder != null ? (JefColor) rowObject.rowBackgroundColorCustomHolder.getValue() : null; //Sort the item if necessary.// if(lastSortedColumnIndex == -1) { tableItem = index == -1 ? new TableItem(table, 0, index = table.getItemCount()) : new TableItem(table, 0, index); }//if// else { TableItem[] rows = table.getItems(); ColumnData columnData = (ColumnData) getColumn(lastSortedColumnIndex); String newText = (String) columnData.cellText.getValue(item); for(int rowIndex = 0; (tableItem == null) && (rowIndex < rows.length); rowIndex++) { String nextText = rows[rowIndex].getText(lastSortedColumnIndex); if(!requiresSwap(newText, nextText, isSortReversed)) { index = rowIndex; tableItem = new TableItem(table, 0, index); }//if// }//for// if(tableItem == null) { index = rows.length; tableItem = new TableItem(table, 0, index); }//if// }//else// //Determine the correct foreground color.// if(rowForegroundColor == null) { //If odd use the alt color if available, otherwise the primary, otherwise null.// if((index % 2) == 1) { if(rowForegroundColorAltHolder != null) { rowForegroundColor = (JefColor) rowForegroundColorAltHolder.getValue(); }//if// else if(rowForegroundColorHolder != null) { rowForegroundColor = (JefColor) rowForegroundColorHolder.getValue(); }//else if// }//if// else if(rowForegroundColorHolder != null) { rowForegroundColor = (JefColor) rowForegroundColorHolder.getValue(); }//else if// }//if// //Determine the correct background color.// if(rowBackgroundColor == null) { //If odd use the alt color if available, otherwise the primary, otherwise null.// if((index % 2) == 1) { if(rowBackgroundColorAltHolder != null) { rowBackgroundColor = (JefColor) rowBackgroundColorAltHolder.getValue(); }//if// else if(rowBackgroundColorHolder != null) { rowBackgroundColor = (JefColor) rowBackgroundColorHolder.getValue(); }//else if// }//if// else if(rowBackgroundColorHolder != null) { rowBackgroundColor = (JefColor) rowBackgroundColorHolder.getValue(); }//else if// }//if// //Set the row coloring.// tableItem.setBackground(createColor(rowBackgroundColor)); tableItem.setForeground(createColor(rowForegroundColor)); //Set the row font.// if(rowObject.rowFontHolder != null) { tableItem.setFont(createFont((JefFont[]) rowObject.rowFontHolder.getValue())); }//if// //Set the column data for each column.// for(int columnIndex = 0; columnIndex < columnCount; columnIndex++) { ColumnData columnData = (ColumnData) getColumn(columnIndex); String cellText = (String) columnData.cellText.getValue(item); tableItem.setText(columnIndex, cellText == null ? "" : cellText); tableItem.setImage(columnIndex, createImage((JefImage) columnData.cellImageHolder.getValue(item))); tableItem.setBackground(columnIndex, createColor((JefColor) columnData.backgroundColorHolder.getValue(item))); tableItem.setForeground(columnIndex, createColor((JefColor) columnData.foregroundColorHolder.getValue(item))); tableItem.setFont(columnIndex, createFont((JefFont[]) columnData.fontHolder.getValue(item))); }//for// //Save a reference to the row object from the table item.// tableItem.setData(rowObject); //If this wasn't the last row and alternate colors are provided then we must update the coloring of the subsiquent rows.// if((rowBackgroundColorAltHolder != null || rowForegroundColorAltHolder != null) && index < table.getItemCount() + 1) { updateRowColors(index + 1); }//if// }//try// finally { suspendResizeEvents = false; suspendUpdateTableEditors(false); }//finally// if(getAutoFit()) { int controlWidth = table.getClientArea().width; if(controlWidth != lastControlWidth) { lastControlWidth = controlWidth; fit(); }//if// }//if// updateTableEditors(); layoutRenderers(); return tableItem; }//controlAddRow()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlRemoveRow(java.lang.Object) */ protected void controlRemoveRow(Object tableItemData) { suspendUpdateTableEditors(true); try { TableItem tableItem = (TableItem) tableItemData; //Dispose of any color, font, or image resources used.// destroyImage(tableItem.getImage()); destroyColor(tableItem.getBackground()); destroyColor(tableItem.getForeground()); destroyFont(tableItem.getFont()); for(int columnIndex = 0; columnIndex < getSwtTable().getColumnCount(); columnIndex++) { destroyImage(tableItem.getImage(columnIndex)); destroyColor(tableItem.getBackground(columnIndex)); destroyColor(tableItem.getForeground(columnIndex)); destroyFont(tableItem.getFont(columnIndex)); }//for// tableItem.dispose(); }//try// finally { suspendUpdateTableEditors(false); }//finally// if(getAutoFit()) { int controlWidth = getSwtTable().getClientArea().width; if(controlWidth != lastControlWidth) { lastControlWidth = controlWidth; fit(); }//if// }//if// updateTableEditors(); layoutRenderers(); updateRowColors(0); //TODO: Is it easier to update all, or figure out what the index of the item was? }//controlRemoveRow()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlAddColumn(com.foundation.view.swt.TableComponent.AbstractColumn) */ protected void controlAddColumn(AbstractColumn columnData) { if(!getSwtTable().isDisposed()) { internalColumnAdded((ColumnData) columnData, new TableColumn(getSwtTable(), 0)); }//if// }//controlAddColumn()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlAddColumn(com.foundation.view.swt.TableComponent.AbstractColumn, int) */ protected void controlAddColumn(AbstractColumn columnData, int columnIndex) { if(!getSwtTable().isDisposed()) { internalColumnAdded((ColumnData) columnData, new TableColumn(getSwtTable(), 0, columnIndex)); }//if// }//controlAddColumn()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlRemoveColumn(int) */ protected void controlRemoveColumn(int columnIndex) { if(!getSwtTable().getColumn(columnIndex).isDisposed()) { getSwtTable().getColumn(columnIndex).dispose(); }//if// }//controlRemoveColumn()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlAddSelection(java.lang.Object) */ protected void controlAddSelection(Object tableItemData) { if(!getSwtTable().isDisposed()) { TableItem tableItem = (TableItem) tableItemData; int count = getSwtTable().getSelectionCount(); getSwtTable().select(getSwtTable().indexOf(tableItem)); if(count == 0) { getSwtTable().showItem(tableItem); }//if// }//if// }//controlAddSelection()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlRemoveSelection(java.lang.Object) */ protected void controlRemoveSelection(Object tableItemData) { if(!getSwtTable().isDisposed()) { TableItem tableItem = (TableItem) tableItemData; getSwtTable().deselect(getSwtTable().indexOf(tableItem)); }//if// }//controlRemoveSelection()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlRemoveAllSelections() */ protected void controlRemoveAllSelections() { if(!getSwtTable().isDisposed()) { getSwtTable().deselectAll(); }//if// }//controlRemoveAllSelections()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlSetSelection(java.lang.Object) */ protected void controlSetSelection(Object tableItemData) { if(!getSwtTable().isDisposed()) { TableItem tableItem = (TableItem) tableItemData; getSwtTable().setSelection(getSwtTable().indexOf(tableItem)); getSwtTable().showItem(tableItem); }//if// }//controlSetSelection()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlSetSelections(com.common.util.IList) */ protected void controlSetSelections(IList tableItemData) { if(!getSwtTable().isDisposed()) { TableItem[] tableItems = new TableItem[tableItemData.getSize()]; //Collect the table items for the selected rows.// for(int index = 0; index < tableItems.length; index++) { tableItems[index] = (TableItem) tableItemData.get(index); }//for// getSwtTable().setSelection(tableItems); if(tableItems.length > 0) { getSwtTable().showItem(tableItems[0]); }//if// }//if// }//controlSetSelections()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlGetSelection() */ protected TableRowObject controlGetSelection() { int selectionIndex = getSwtTable().getSelectionIndex(); return selectionIndex == -1 ? null : (SimpleTableRowObject) getSwtTable().getItem(selectionIndex).getData(); }//controlGetSelection()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlGetSelections() */ protected TableRowObject[] controlGetSelections() { TableRowObject[] results = null; if(getSwtTable().getSelectionCount() > 0) { TableItem[] tableItems = getSwtTable().getSelection(); results = new TableRowObject[tableItems.length]; for(int index = 0; index < tableItems.length; index++) { results[index] = (SimpleTableRowObject) tableItems[index].getData(); }//for// }//if// return results; }//controlGetSelections()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#controlIsSelected(java.lang.Object) */ protected boolean controlIsSelected(Object tableItemData) { TableItem tableItem = (TableItem) tableItemData; return getSwtTable().isSelected(getSwtTable().indexOf(tableItem)); }//controlIsSelected()// /* (non-Javadoc) * @see com.foundation.view.swt.CollectionComponent#controlGetSelectionCount() */ protected int controlGetSelectionCount() { return getSwtTable().getSelectionCount(); }//controlGetSelectionCount()// /* (non-Javadoc) * @see com.foundation.view.swt.TableComponent#forceSelectionEvent() */ protected void forceSelectionEvent() { if(!getSwtTable().isDisposed()) { widgetSelected(null); }//if// }//forceSelectionEvent()// /* (non-Javadoc) * @see com.foundation.view.swt.CollectionComponent#getAutoSynchronizeSelectionDelay() */ protected long getAutoSynchronizeSelectionDelay() { return disableAutoSynchronizeDelay ? 0 : super.getAutoSynchronizeSelectionDelay(); }//getAutoSynchronizeSelectionDelay()// /* (non-Javadoc) * @see com.foundation.view.swt.CollectionComponent#internalPreChangeCollection() */ protected void internalPreChangeCollection() { super.internalPreChangeCollection(); suspendUpdateTableEditors(true); suspendLayoutRenderers = true; }//internalPreChangeCollection()// /* (non-Javadoc) * @see com.foundation.view.swt.CollectionComponent#internalPostChangeCollection() */ protected void internalPostChangeCollection() { super.internalPostChangeCollection(); suspendUpdateTableEditors(false); suspendLayoutRenderers = false; //Update the editors and renderers after large scale changes to the collection.// updateTableEditors(); layoutRenderers(); }//internalPostChangeCollection()// /** * Determines whether to suspend the table editor updating. * @return Whether to suspend table editor updating. */ protected boolean suspendUpdateTableEditors() { return suspendUpdateTableEditorsCount > 0; }//suspendUpdateTableEditors()// /** * Suspends or un-suspends the table editor updating. * @param suspend Whether to suspend the updating, or restart updating. There must be a call to un-suspend for each call to suspend. */ protected void suspendUpdateTableEditors(boolean suspend) { if(suspend) { suspendUpdateTableEditorsCount++; }//if// else { suspendUpdateTableEditorsCount--; }//else// }//suspendUpdateTableEditors()// /** * Releases the table editors for the given item. * @param tableItem The table item whose editors will be released. */ protected void releaseTableEditors(TableItem tableItem) { int columnCount = getColumnCount(); //Release the editors for the row and each column.// for(int columnIndex = 0; columnIndex < columnCount; columnIndex++) { TableColumn tableColumn = getSwtTable().getColumn(columnIndex); ColumnData columnData = (ColumnData) tableColumn.getData(); TableEditor tableEditor = columnData.removeTableEditor(tableItem); if(tableEditor != null) { CellComponent cellComponent = ((CellComponent.CellControl) tableEditor.getEditor().getData()).getCellComponent(); cellComponent.destroyCellControl(tableEditor.getEditor()); tableEditor.dispose(); }//if// }//for// }//releaseTableEditors()// /** * Creates the table editors for the given item. * @param tableItem The table item whose editors will be created. */ protected void createTableEditors(TableItem tableItem) { int columnCount = getColumnCount(); SimpleTableRowObject rowObject = (SimpleTableRowObject) tableItem.getData(); //Check for a null row object indicating that the update is due to a resize due to the creation of a table item which has not yet been initialized.// if(rowObject != null) { //Release the editors for the row and each column.// for(int columnIndex = 0; columnIndex < columnCount; columnIndex++) { TableColumn tableColumn = getSwtTable().getColumn(columnIndex); ColumnData columnData = (ColumnData) tableColumn.getData(); if(columnData.hasComponentCapableRenderers()) { CellComponent cellComponent = columnData.getCellComponent(rowObject.value != null ? rowObject.value.getClass() : null); //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) && ((getSwtTable().getSelectionCount() == 1) && (getSwtTable().getItem(getSwtTable().getSelectionIndex()) == tableItem))))) { Control control = cellComponent.createCellControl(rowObject.value); TableEditor tableEditor = new TableEditor(getSwtTable()); Point size; tableEditor.setEditor(control, tableItem, columnIndex); columnData.setTableEditor(tableItem, tableEditor); tableEditor.grabHorizontal = cellComponent.grabHorizontalSpace(); tableEditor.grabVertical = cellComponent.grabVerticalSpace(); tableEditor.horizontalAlignment = cellComponent.getHorizontalAlignment(); tableEditor.verticalAlignment = cellComponent.getVerticalAlignment(); size = tableEditor.getEditor().computeSize(SWT.DEFAULT, SWT.DEFAULT); if(size.y > minimumTableEditorHeight) { minimumTableEditorHeight = size.y; }//if// }//if// }//if// }//for// }//if// }//createTableEditors()// /** * Updates the table editors so that all visible rows have appropriate table editors when the table scrolls. */ protected void updateTableEditors() { if(!suspendUpdateTableEditors()) { int topIndex = getSwtTable().getTopIndex(); TableItem item = (topIndex >= 0 && topIndex < getSwtTable().getItemCount()) ? getSwtTable().getItem(topIndex) : null; IIterator iterator; if(item != null) { Rectangle clientArea = getSwtTable().getClientArea(); int visibleRowCount = (int) Math.ceil(clientArea.height / (double) getSwtTable().getItemHeight()); LiteHashSet visibleTableItems = new LiteHashSet(visibleRowCount); //Collect the visible table items.// for(int index = topIndex + visibleRowCount < getSwtTable().getItemCount() ? topIndex + visibleRowCount : getSwtTable().getItemCount() - 1; index >= topIndex; index--) { visibleTableItems.add(getSwtTable().getItem(index)); }//for// if(currentlyVisibleTableItems != null) { iterator = currentlyVisibleTableItems.iterator(); //Release the editors for items no longer visible.// while(iterator.hasNext()) { TableItem next = (TableItem) iterator.next(); if(!visibleTableItems.containsValue(next)) { releaseTableEditors(next); }//if// }//while// iterator = visibleTableItems.iterator(); //Setup the editors for newly visible items.// while(iterator.hasNext()) { TableItem next = (TableItem) iterator.next(); if(!currentlyVisibleTableItems.containsValue(next)) { createTableEditors(next); }//if// }//while// }//if// else { iterator = visibleTableItems.iterator(); //Setup the editors for newly visible items.// while(iterator.hasNext()) { TableItem next = (TableItem) iterator.next(); createTableEditors(next); }//while// }//else// currentlyVisibleTableItems = visibleTableItems; }//if// else if(currentlyVisibleTableItems != null) { iterator = currentlyVisibleTableItems.iterator(); //Setup the editors for newly visible items.// while(iterator.hasNext()) { TableItem next = (TableItem) iterator.next(); releaseTableEditors(next); }//while// currentlyVisibleTableItems = null; }//else if// }//if// }//updateTableEditors()// /** * Forces the renderers to reposition themselves since SWT has a bug where by the renderers are not updated on table adds, removes, or row height resizing. */ protected void layoutRenderers() { if(!suspendLayoutRenderers) { stopRendering(); try { for(int columnIndex = getColumnCount() - 1; columnIndex >= 0; columnIndex--) { ((ColumnData) getColumn(columnIndex)).layoutRenderers(); }//for// }//try// finally { startRendering(); redraw(); }//finally// }//if// }//layoutRenderers()// /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent) */ public void widgetDefaultSelected(SelectionEvent event) { if(event.widget == getSwtTable()) { //Fix a bug in the table where a double click event is fired twice.// if(lastDoubleClickTime != event.time) { if(getDoubleClickMethod() != null) { //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.// doDoubleClick(); }//try// finally { //Reset the delay and send the messages.// disableAutoSynchronizeDelay = false; }//finally// lastDoubleClickTime = event.time; }//if// else { widgetSelected(event); }//else// }//if// }//if// else if(event.widget == getSwtTable().getVerticalBar()) { updateTableEditors(); }//else// }//widgetDefaultSelected()// /* (non-Javadoc) * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent) */ public void widgetSelected(SelectionEvent event) { if(!suspendSelectionEvents && ((event == null) || (event.widget == getSwtTable()))) { synchronizeSelection(); }//if// else if(event.widget == getSwtTable().getVerticalBar()) { updateTableEditors(); }//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) { int controlWidth = getSwtTable().getClientArea().width; if(controlWidth != lastControlWidth) { lastControlWidth = controlWidth; if(getAutoFit()) { fit(); }//if// else if(getFillOnResize()) { fill(); }//else if// }//if// updateTableEditors(); }//if// }//controlResized()// }//SimpleTable//