blob: 6e8d857055f6c2ac162befbebaee82aed208f98c [file] [log] [blame]
/*
* Copyright (C) 2005 David Orme <djo@coconut-palm-software.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* David Orme - Initial API and implementation
* Coconut Palm Software, Inc. - API cleanup
*/
package org.eclipse.jface.examples.databinding.compositetable;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.LinkedList;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.widgets.Canvas;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Layout;
/**
* Class CompositeTable. n. (1) An SWT virtual table control that extends Composite.
* (2) An SWT virtual table control that is composed of many Composites, each representing
* a header or a row, one below the other.<p>
*
* CompositeTable is designed specifically to work nicely in the Eclipse Visual Editor,
* but it is equally easy to use in hand-coded layouts.<p>
*
* <b>Synopsis:</b><p>
*
* In order to edit anything, one must:<p>
*
* <ul>
* <li>Extend Composite or Canvas and create an object that can be duplicated to
* represent the rows in your table.
* <li>Optionally, extend Composite or Canvas and create a header object in the
* same way.
* <li>If the canvas and row objects do not have a layout manager, the CompositeTable
* will automatically supply one that lays out child controls in a visual table. If
* they have a layout manager, CompositeTable will let them use that.
* <li>Create a CompositeTable object, either using VE or using hand-coded SWT.
* <li>Drop the header (if applicable), then the row object on the CompositeTable or
* simply write code that creates instances of these objects in that order as child
* controls of your CompositeTable.
* <li>Set the RunTime property to "true". Your control is now "live."
* <li>Add a RowConstructionListener if you need to add event handlers to individual row
* controls when a row is created.
* <li>Add a RowContentProvider that knows how to put data into your row object's
* controls on demand.
* <li>Add a RowFocusListener to validate and save changed data.
* <li>Set the NumRowsInCollection property to the number of rows in the underlying data
* structure.
* </ul>
*
* Detailed description:<p>
*
* This control is designed to work inside of the Eclipse Visual Editor. To use it,
* drop one on the design surface. (Even though it extends Canvas, it does not make
* sense to put a layout manager on it.)<p>
*
* Next create one or two new custom controls by using the Visual Editor to extend
* Composite. If you create one custom control, it will be used as the prototype
* for all rows that will be displayed in the table. If you create two, the first
* one will be used as a prototype for the header and the second one will be used
* as a prototype for the rows.<p>
*
* If these custom controls are not given layout managers (null layout), then
* CompositeTable will automatically detect this situation and will supply its own
* layout manager that will automatically lay out the children of these controls in
* columns to form a table. However, if you supply layout managers for your header
* prototype and row prototype objects, CompositeTable will respect your choice.
* If you use CompositeTable's built-in layout manager, then the weights property
* will be used to determine what percentage of the total width will be allocated
* to each column. If this property is not set or if the sum of their elements
* does not equal 100, the columns are created as equal sizes.<p>
*
* Once you have created your (optional) Header and Row custom controls, simply drop
* them onto your CompositeTable control in VE. The first of these two custom
* controls to be instantiated in your code will be interpreted by the CompositeTable
* as the header control and the second will be interpreted as the row control.<p>
*
* Now that you have defined the (optional) header and row, you can switch your
* CompositeTable into run mode and use it. This is done by switching the RunTime
* property to true.<p>
*
* Once in run mode, all of the CompositeTable's properties will be active. In order
* to use it, set the NumRowsInCollection property to the number of rows in the
* collection you want to display. And add a RefreshContentProvider, which will
* be called whenever CompositeTable needs to refresh a particular row.<p>
*
* Please refer to the remainder of the JavaDoc for information on the remaining
* properties and events.<p>
*
* Although this control extends Composite, it is not intended to be subclassed
* except within its own implementation and it makes no sense to set a layout
* manager on it (although as discussed above, the child controls may have layout
* managers).
*
* @author djo
* @since 3.2
* TODO: eliminate flicker when scrolling backwards
*/
public class CompositeTable extends Canvas {
// Property fields here
private boolean runTime = false;
private int[] weights = new int[0];
private int numRowsInCollection = 0;
private int maxRowsVisible = Integer.MAX_VALUE;
// Private fields here
private Constructor headerConstructor = null;
private Control headerControl = null;
private Constructor rowConstructor = null;
private Control rowControl = null;
private InternalCompositeTable contentPane = null;
/**
* Constructor CompositeTable. Construct a CompositeTable control. CompositeTable
* accepts the same style bits as the SWT Canvas.
*
* @param parent The SWT parent control.
* @param style Style bits. These are the same as Canvas
*/
public CompositeTable(Composite parent, int style) {
super(parent, style);
setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_LIST_BACKGROUND));
setLayout(new Layout() {
protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) {
if (headerControl == null && rowControl == null) {
return new Point(2, 20);
}
Point headerSize = new Point(0, 0);
if (headerControl != null) {
headerSize = headerControl.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
Point rowSize = new Point(0, 0);
if (rowControl != null) {
rowSize = rowControl.computeSize(SWT.DEFAULT, SWT.DEFAULT);
}
Point result = new Point(Math.max(headerSize.x, rowSize.x), headerSize.y + rowSize.y);
return result;
}
protected void layout(Composite composite, boolean flushCache) {
resize();
}
});
}
/* (non-Javadoc)
* @see org.eclipse.swt.widgets.Control#setBackground(org.eclipse.swt.graphics.Color)
*/
public void setBackground(Color color) {
super.setBackground(color);
if (contentPane != null) {
contentPane.setBackground(color);
}
}
private int numChildrenLastTime = 0;
/** (non-API)
* Method resize. Resize this table's contents. Called from within the custom
* layout manager.
*/
protected final void resize() {
if (isRunTime()) {
int childrenLength = getChildren().length;
if (numChildrenLastTime != childrenLength) {
resizeAndRecordPrototypeRows();
showPrototypes(false);
contentPane.dispose();
contentPane = new InternalCompositeTable(this, SWT.NULL);
}
updateVisibleRows();
} else {
resizeAndRecordPrototypeRows();
}
}
/** (non-API)
* Method updateVisibleRows. Makes sure that the content pane is displaying the correct
* number of visible rows given the control's size. Called from within #resize.
*/
protected void updateVisibleRows() {
if (contentPane == null) {
switchToRunMode();
}
Point size = getSize();
contentPane.setBounds(0, 0, size.x, size.y);
}
/**
* Switch from design mode where prototype header/row objects can be dropped on the
* control into run mode where all of the properties do what you would expect.
*/
private void switchToRunMode() {
showPrototypes(false);
contentPane = new InternalCompositeTable(this, SWT.NULL);
}
/**
* Switch back to design mode so that the prototype header/row objects may be manipulated
* directly in a GUI design tool.
*/
private void switchToDesignMode(){
contentPane.dispose();
contentPane = null;
showPrototypes(true);
resizeAndRecordPrototypeRows();
}
/**
* Turns display of the prototype objects on or off.
*
* @param newValue true of the prototype objects should be displayed; false otherwise.
*/
private void showPrototypes(boolean newValue) {
if (headerControl != null) {
headerControl.setVisible(newValue);
}
if (rowControl != null) {
rowControl.setVisible(newValue);
}
}
/**
* (non-API) Method resizeAndRecordPrototypeRows. Figure out what child
* controls are the header and row prototype rows respectively and resize
* them so they occupy the entire width and their preferred height.
*/
protected void resizeAndRecordPrototypeRows() {
Control[] children = getChildren();
ArrayList realChildren = new ArrayList();
Control[] finalChildren = children;
// Find first two prototypes
for (int i = 0; i < children.length; i++) {
if (children[i] instanceof InternalCompositeTable) {
continue;
}
if (realChildren.size() < 2) {
realChildren.add(children[i]);
}
}
finalChildren = (Control[]) realChildren.toArray(new Control[realChildren.size()]);
if (finalChildren.length == 0) {
headerConstructor = null;
headerControl = null;
rowConstructor = null;
rowControl = null;
return;
}
// Get a constructor for the header and/or the row prototype
headerConstructor = null;
headerControl = null;
rowConstructor = null;
rowControl = null;
if (finalChildren.length == 1) {
try {
rowControl = (Composite) finalChildren[0];
rowConstructor = finalChildren[0].getClass().getConstructor(new Class[] {Composite.class, Integer.TYPE});
} catch (Exception e) {
}
} else {
try {
headerConstructor = finalChildren[0].getClass().getConstructor(new Class[] {Composite.class, Integer.TYPE});
headerControl = finalChildren[0];
rowConstructor = finalChildren[1].getClass().getConstructor(new Class[] {Composite.class, Integer.TYPE});
rowControl = finalChildren[1];
} catch (Exception e) {
}
}
// Now actually resize the children
int top=0;
int width = getSize().x;
for (int i=0; i < finalChildren.length; ++i) {
int height = 50;
if (finalChildren[i] instanceof Composite) {
Composite child = (Composite) finalChildren[i];
if (child.getLayout() == null) {
height = layoutHeaderOrRow(child);
} else {
height = finalChildren[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
}
} else {
height = finalChildren[i].computeSize(SWT.DEFAULT, SWT.DEFAULT).y;
}
finalChildren[i].setBounds(0, top, width, height);
top += height;
}
numChildrenLastTime = children.length;
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
getParent().layout(true);
}
});
}
/**(non-API)
* Method layoutHeaderOrRow. If a header or row object does not have a layout manager, this
* method will automatically be called to layout the child controls of that header or row
* object.
*
* @param child The child object to layout.
* @return the height of the header or row
*/
int layoutHeaderOrRow(Composite child) {
Control[] children = child.getChildren();
if (children.length == 0) {
return 50;
}
int[] weights = this.weights;
weights = checkWeights(weights, children.length);
int maxHeight = 0;
for (int i = 0; i < children.length; i++) {
int height = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, true).y;
if (maxHeight < height) {
maxHeight = height;
}
}
++maxHeight;
int widthRemaining = child.getParent().getSize().x;
int totalSize = widthRemaining;
for (int i = 0; i < children.length-1; i++) {
int left = totalSize - widthRemaining;
int desiredHeight = children[i].computeSize(SWT.DEFAULT, SWT.DEFAULT, false).y;
int top = maxHeight - desiredHeight-1;
int width = (int)(((float)weights[i])/100 * totalSize);
children[i].setBounds(left+2, top, width-4, desiredHeight);
widthRemaining -= width;
}
int left = totalSize - widthRemaining;
int desiredHeight = children[children.length-1].computeSize(SWT.DEFAULT, SWT.DEFAULT, false).y;
int top = maxHeight - desiredHeight-1;
children[children.length-1].setBounds(left+2, top, widthRemaining-4, desiredHeight);
return maxHeight;
}
/**
* Compute and return a weights array where each weight is the percentage the corresponding
* column should occupy of the entire control width. If the elements in the supplied weights
* array add up to 100 and the length of the array is the same as the number of columns, the
* supplied weights array is used. Otherwise, this method computes and returns a weights
* array that makes each column an equal size.
*
* @param weights The default or user-supplied weights array.
* @param numChildren The number of child controls.
* @return The weights array that will be used by the layout manager.
*/
private int[] checkWeights(int[] weights, int numChildren) {
if (weights.length == numChildren) {
int sum = 0;
for (int i = 0; i < weights.length; i++) {
sum += weights[i];
}
if (sum == 100) {
return weights;
}
}
// Either the number of weights doesn't match or they don't add up.
// Compute something sane and return that instead.
int[] result = new int[numChildren];
int weight = 100 / numChildren;
int extra = 100 % numChildren;
for (int i = 0; i < result.length-1; i++) {
result[i] = weight;
}
result[numChildren-1] = weight + extra;
return result;
}
/**
* Method isRunTime. Returns if the CompositeTable is in run time mode as opposed
* to design time mode. In design time mode, the only permitted operations are to
* add or remove child Composites to be used as the header and/or row prototype objects.
*
* @return true if this CompositeTable is in run mode. false otherwise.
*/
public boolean isRunTime() {
return runTime;
}
/**
* Method setRunTime. Turns run-time mode on or off. When run-time mode is off,
* CompositeTable ignores most property operations and will accept prototype child
* controls to be added. When run-time mode is on, the prototype controls are
* interpreted and all properties become active.
*
* @param runTime true if run-time mode should be enabled; false otherwise.
*/
public void setRunTime(boolean runTime) {
if (this.runTime != runTime) {
this.runTime = runTime;
if (runTime) {
if (rowControl == null) {
resizeAndRecordPrototypeRows();
}
switchToRunMode();
} else {
switchToDesignMode();
}
}
}
/**
* Method getWeights. Returns an array representing the percentage of the total width
* each column is allocated or null if no weights have been specified. This property is
* ignored if the programmer has set a layout manager on the header and/or the row
* prototype objects.
*
* @return the current weights array or null if no weights have been specified.
*/
public int[] getWeights() {
return weights;
}
/**
* Method setWeights. Specifies the percentage of the total width that will be allocated
* to each column. This property is ignored if the programmer has set a layout manager
* on the header and/or the row prototype objects.<p>
*
* The number of elements in the array must match the number of columns and the sum of
* all elements must equal 100. If either of these constraints is not true, this property
* will be ignored and all columns will be created equal in width.
*
* @param weights the weights to use if the CompositeTable is automatically laying out controls.
*/
public void setWeights(int[] weights) {
this.weights = weights;
if (contentPane != null) {
contentPane.setWeights();
}
}
boolean gridLinesOn = true;
/**
* Method isGridLinesOn. Returns if the CompositeTable will draw grid lines on the header
* and row Composite objects. This property is ignored if the programmer has set a layout
* manager on the header and/or the row prototype objects.
*
* @return true if the CompositeTable will draw grid lines; false otherwise.
*/
public boolean isGridLinesOn() {
return gridLinesOn;
}
/**
* Method setGridLinesOn. Sets if the CompositeTable will draw grid lines on the header and
* row Composite objects. This property is ignored if the programmer has set a layout
* manager on the header and/or the row prototype objects.
*
* @param gridLinesOn true if the CompositeTable will draw grid lines; false otherwise.
*/
public void setGridLinesOn(boolean gridLinesOn) {
this.gridLinesOn = gridLinesOn;
}
String insertHint = "Press <INS> to insert new data."; //$NON-NLS-1$
/**
* Returns the hint string that will be displayed when there are no rows in the table.
*
* @return The hint string that will be displayed when there are no rows in the table.
*/
public String getInsertHint() {
return insertHint;
}
/**
* Sets the hint string that will be displayed when there are no rows in the table. The
* default value is "Press &lt;INS> to insert a new row."
*
* @param newHint
*/
public void setInsertHint(String newHint) {
this.insertHint = newHint;
}
/**
* Method getMaxRowsVisible. Returns the maximum number of rows that will be permitted
* in the table at once. For example, setting this property to 1 will have the effect of
* creating a single editing area with a scroll bar on the right allowing the user to scroll
* through all rows using either the mouse or the PgUp/PgDn keys. The default value is
* Integer.MAX_VALUE.
*
* @return the maximum number of rows that are permitted to be visible at one time, regardless
* of the control's size.
*/
public int getMaxRowsVisible() {
return maxRowsVisible;
}
/**
* Method setMaxRowsVisible. Sets the maximum number of rows that will be permitted
* in the table at once. For example, setting this property to 1 will have the effect of
* creating a single editing area with a scroll bar on the right allowing the user to scroll
* through all rows using either the mouse or the PgUp/PgDn keys. The default value is
* Integer.MAX_VALUE.
*
* @param maxRowsVisible the maximum number of rows that are permitted to be visible at one time, regardless
* of the control's size.
*/
public void setMaxRowsVisible(int maxRowsVisible) {
this.maxRowsVisible = maxRowsVisible;
if (contentPane != null) {
contentPane.setMaxRowsVisible(maxRowsVisible);
}
}
/**
* Method getNumRowsVisible. Returns the actual number of rows that are currently visible.
* Normally CompositeTable displays as many rows as will fit vertically given the control's
* size. This value can be clamped to a maximum using the MaxRowsVisible property.
*
* @return the actual number of rows that are currently visible.
*/
public int getNumRowsVisible() {
if (contentPane != null)
return contentPane.getNumRowsVisible();
return -1;
}
/**
* Method getNumRowsInCollection. Returns the number of rows in the data structure that is
* being edited.
*
* @return the number of rows in the underlying data structure.
*/
public int getNumRowsInCollection() {
return numRowsInCollection;
}
/**
* Method setNumRowsInCollection. Sets the number of rows in the data structure that is
* being edited.
*
* @param numRowsInCollection the number of rows represented by the underlying data structure.
*/
public void setNumRowsInCollection(int numRowsInCollection) {
this.numRowsInCollection = numRowsInCollection;
if (contentPane != null) {
contentPane.setNumRowsInCollection(numRowsInCollection);
}
}
private int topRow=0;
/**
* Method getTopRow. Return the number of the line that is being displayed in the top row
* of the CompositeTable editor (0-based).
*
* @return the number of the top line.
*/
public int getTopRow() {
if (contentPane != null) {
return contentPane.getTopRow();
}
return topRow;
}
/**
* Method setTopRow. Set the number of the line that is being displayed in the top row
* of the CompositeTable editor (0-based). If the new top row is not equal to the current
* top row, the table will automatically be scrolled to the new position. This number must
* be greater than 0 and less than NumRowsInCollection.
*
* @param topRow the line number of the new top row.
*/
public void setTopRow(int topRow) {
this.topRow = topRow;
if (contentPane != null) {
contentPane.setTopRow(topRow);
}
}
/**
* Method refreshAllRows. Refresh all visible rows in the CompositeTable from the
* original data.
*/
public void refreshAllRows() {
if (contentPane != null) {
contentPane.updateVisibleRows();
contentPane.refreshAllRows();
}
}
/**
* Method getCurrentColumn. Returns the column number of the currently-focused column
* (0-based).
*
* @return the column number of the currently-focused column.
*/
public int getCurrentColumn() {
if (contentPane == null) {
return -1;
}
return getSelection().x;
}
/**
* Method setCurrentColumn. Sets the column number of the currently-focused column
* (0-based).
*
* @param column The new column to focus.
*/
public void setCurrentColumn(int column) {
setSelection(column, getCurrentRow());
}
/**
* Method getCurrentRow. Returns the current row number as an offset from the top of the
* table window. In order to get the current row in the underlying data structure, compute
* getTopRow() + getCurrentRow().
*
* @return the current row number as an offset from the top of the table window.
*/
public int getCurrentRow() {
if (contentPane == null) {
return -1;
}
return getSelection().y;
}
/**
* Method setCurrentRow. Sets the current row number as an offset from the top of the
* table window. In order to get the current row in the underlying data structure, compute
* getTopRow() + getCurrentRow().
*
* @param row the current row number as an offset from the top of the table window.
*/
public void setCurrentRow(int row) {
setSelection(getCurrentColumn(), row);
}
/**
* Method getCurrentRowControl. Returns the SWT control that displays the current row.
*
* @return Control the current row control.
*/
public Control getCurrentRowControl() {
return contentPane.getCurrentRowControl();
}
/**
* Method getSelection. Returns the currently-selected (column, row) pair where the row
* specifies the offset from the top of the table window. In order to get the current
* row in the underlying data structure, use getSelection().y + getCurrentRow().
*
* @return the currently-selected (column, row) pair where the row specifies the offset
* from the top of the table window.
*/
public Point getSelection() {
if (contentPane == null) {
return null;
}
return contentPane.getSelection();
}
/**
* Method setSelection. Sets the currently-selected (column, row) pair where the row
* specifies the offset from the top of the table window. In order to get the current
* row in the underlying data structure, use getSelection().y + getCurrentRow().
*
* @param selection the (column, row) to select
*/
public void setSelection(Point selection) {
setSelection(selection.x, selection.y);
}
/**
* Method setSelection. Sets the currently-selected (column, row) pair where the row
* specifies the offset from the top of the table window. In order to get the current
* row in the underlying data structure, use getSelection().y + getCurrentRow().
*
* @param column the column to select
* @param row the row to select as an offset from the top of the window
*/
public void setSelection(int column, int row) {
if (contentPane == null) {
return;
}
contentPane.setSelection(column, row);
}
/** (non-API)
* Method getHeaderConstructor. Returns the Constructor object used internally to
* construct the table's header or null if there is none.
*
* @return the header's constructor.
*/
public Constructor getHeaderConstructor() {
return headerConstructor;
}
/** (non-API)
* Method getRowConstructor. Returns the Constructor object used internally to
* construct each row object.
*
* @return the rows' constructor
*/
public Constructor getRowConstructor() {
return rowConstructor;
}
/** (non-API)
* Method getHeaderControl. Returns the prototype header control.
*
* @return the prototype header control.
*/
public Control getHeaderControl() {
return headerControl;
}
/** (non-API)
* Method getRowControl. Returns the prototype row control.
*
* @return the prototype row control.
*/
public Control getRowControl() {
return rowControl;
}
LinkedList contentProviders = new LinkedList();
/**
* Method addRowContentProvider. Adds the specified content provider to the list of
* content providers that will be called when a row needs to be filled with data.
* Most of the time it only makes sense to add a single one.
*
* @param contentProvider The content provider to add.
*/
public void addRowContentProvider(IRowContentProvider contentProvider) {
contentProviders.add(contentProvider);
}
/**
* Method removeRowContentProvider. Removes the specified content provider from the list of
* content providers that will be called when a row needs to be filled with data.
*
* @param contentProvider The content provider to remove.
*/
public void removeRowContentProvider(IRowContentProvider contentProvider) {
contentProviders.remove(contentProvider);
}
LinkedList rowFocusListeners = new LinkedList();
/**
* Method addRowListener. Adds the specified listener to the set of listeners that
* will be notified when the user wishes to leave a row and when the user has already
* left a row. If any listener vetos leaving a row, the focus remains in the row.
*
* @param rowListener The listener to add.
*/
public void addRowFocusListener(IRowFocusListener rowListener) {
rowFocusListeners.add(rowListener);
}
/**
* Method removeRowListener. Removes the specified listener from the set of listeners that
* will be notified when the user wishes to leave a row and when the user has already
* left a row. If any listener vetos leaving a row, the focus remains in the row.
*
* @param listener The listener to remove.
*/
public void removeRowFocusListener(IRowFocusListener listener) {
rowFocusListeners.remove(listener);
}
LinkedList insertHandlers = new LinkedList();
/**
* Method addInsertHandler. Adds the specified insertHandler to the set of objects that
* will be used to handle insert requests.
*
* @param insertHandler the insertHandler to add.
*/
public void addInsertHandler(IInsertHandler insertHandler) {
insertHandlers.add(insertHandler);
}
/**
* Method removeInsertHandler. Removes the specified insertHandler from the set of objects that
* will be used to handle insert requests.
*
* @param insertHandler the insertHandler to remove.
*/
public void removeInsertHandler(IInsertHandler insertHandler) {
insertHandlers.remove(insertHandler);
}
LinkedList deleteHandlers = new LinkedList();
/**
* Method addDeleteHandler. Adds the specified deleteHandler to the set of objects that
* will be used to handle delete requests.
*
* @param deleteHandler the deleteHandler to add.
*/
public void addDeleteHandler(IDeleteHandler deleteHandler) {
deleteHandlers.add(deleteHandler);
}
/**
* Method removeDeleteHandler. Removes the specified deleteHandler from the set of objects that
* will be used to handle delete requests.
*
* @param deleteHandler the deleteHandler to remove.
*/
public void removeDeleteHandler(IDeleteHandler deleteHandler) {
deleteHandlers.remove(deleteHandler);
}
LinkedList rowConstructionListeners = new LinkedList();
/**
* Method addRowConstructionListener. Adds the specified rowConstructionListener to the set of objects that
* will be used to listen to row construction events.
*
* @param rowConstructionListener the rowConstructionListener to add.
*/
public void addRowConstructionListener(IRowConstructionListener rowConstructionListener) {
rowConstructionListeners.add(rowConstructionListener);
}
/**
* Method removeRowConstructionListener. Removes the specified rowConstructionListener from the set of objects that
* will be used to listen to row construction events.
*
* @param rowConstructionListener the rowConstructionListener to remove.
*/
public void removeRowConstructionListener(IRowConstructionListener rowConstructionListener) {
rowConstructionListeners.remove(rowConstructionListener);
}
boolean deleteEnabled = true;
/**
* Method isDeleteEnabled. Returns if delete is enabled. Deletions are only processed if
* the DeleteEnabled property is true and at least one delete handler has been registered.<p>
*
* The default value is true.
*
* @return true if delete is enabled. false otherwise.
*/
public boolean isDeleteEnabled() {
return deleteEnabled;
}
/**
* Method setDeleteEnabled. Sets if delete is enabled. Deletions are only processed if
* the DeleteEnabled property is true and at least one delete handler has been registered.<p>
*
* The default value is true.
*
* @param deleteEnabled true if delete should be enabled. false otherwise.
*/
public void setDeleteEnabled(boolean deleteEnabled) {
this.deleteEnabled = deleteEnabled;
}
} // @jve:decl-index=0:visual-constraint="10,10"