Skip to main content

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] [List Home]
[platform-ui-dev] Proposing a change to how cell editors are used in TableViewer

Hi,
I am working a lot with Table and TableViewer classes, and have the
following requirements:

If the celleditor control is a combo box, the values list has to be
populated dynamically depending on the data in the selected row
(TableItem).
Also, sometimes, i have to dynamically change the control being offered
as a cell editor depending on the data in the selected TableItem. (eg:
many times i have to dynamically decide between a Text and a Combo
control for that column (both are valid in specific cicumstances).

There is no provision currently in the Eclipse source for doing this,
so i have created a subinterface of the ICellModifier interface, and
have added a hack to the TableViewerImpl class (in
activateCellEditor()). I also had to hack the TableViewer class to use
my class intead of TableViewerImpl. Actually, the change is just of one
line in TableViewerImpl. I am attaching the three source files. Please
tell me if this change is possible to the jface codebase, else i will
have to maintain my code in sync with the jface code.

Thanx in advance
Abeer

P.S.: I am also attaching an application specific implementation of the
INPCellModifier, to illustrate how i am using this interface.

package com.kenati.npeint.tools.cliide.ui.widgets;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellModifier;

/**
 * @author abeer
 */
public interface INPCellModifier extends ICellModifier
{
    public CellEditor getCellEditor(Object element, String property);
}

package com.kenati.npeint.tools.cliide.ui.widgets;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.TableEditor;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.swt.widgets.Widget;

import org.eclipse.jface.util.Assert;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IFontProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.IViewerLabelProvider;
import org.eclipse.jface.viewers.OpenEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerLabel;
import org.eclipse.jface.viewers.ViewerSorter;

/**
 * We are using all the code from the TableViewer class as is since the
 * TableViewer is not intended to be subclassed.
 * We have only two changes in this class
 * TableViewer uses the class TableViewerImpl and we want to use 
 * the class NPTableViewerImpl instead.
 * Also we are using the interface INPCellModifier instead of ICellModifier
 * We need maintain to this class in sync with future releases of Eclipse
 */
public class NPTableViewer extends StructuredViewer {

	/**
	 * Internal table viewer implementation.
	 */
    /**
	 * ****************************
	 * Abeer :) - this is the only diff between TableViewer and NPTableViewer
	 * using NPTableViewerImpl instead of TableViewerImpl
	 * ****************************
     */
	private NPTableViewerImpl tableViewerImpl;

	/**
	 * This viewer's table control.
	 */
	private Table table;

	/**
	 * This viewer's table editor.
	 */
	private TableEditor tableEditor;
/**
 * Creates a table viewer on a newly-created table control under the given parent.
 * The table control is created using the SWT style bits <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>.
 * The viewer has no input, no content provider, a default label provider, 
 * no sorter, and no filters. The table has no columns.
 *
 * @param parent the parent control
 */
public NPTableViewer(Composite parent) {
	this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER);
}
/**
 * Creates a table viewer on a newly-created table control under the given parent.
 * The table control is created using the given style bits.
 * The viewer has no input, no content provider, a default label provider, 
 * no sorter, and no filters. The table has no columns.
 *
 * @param parent the parent control
 * @param style SWT style bits
 */
public NPTableViewer(Composite parent, int style) {
	this(new Table(parent, style));
}
/**
 * Creates a table viewer on the given table control.
 * The viewer has no input, no content provider, a default label provider, 
 * no sorter, and no filters. 
 *
 * @param table the table control
 */
public NPTableViewer(Table table) {
	this.table = table;
	hookControl(table);
	tableEditor = new TableEditor(table);
	initTableViewerImpl();
}
/**
 * Adds the given elements to this table viewer.
 * If this viewer does not have a sorter, the elements are added at the end
 * in the order given; otherwise the elements are inserted at appropriate positions.
 * <p>
 * This method should be called (by the content provider) when elements 
 * have been added to the model, in order to cause the viewer to accurately
 * reflect the model. This method only affects the viewer, not the model.
 * </p>
 *
 * @param elements the elements to add
 */
public void add(Object[] elements) {
	assertElementsNotNull(elements);	
	Object[] filtered = filter(elements);
	for (int i = 0; i < filtered.length; i++){
		Object element = filtered[i];
		int index = indexForElement(element);
		updateItem(new TableItem(getTable(), SWT.NONE, index), element);
	}
}
/**
 * Adds the given element to this table viewer.
 * If this viewer does not have a sorter, the element is added at the end;
 * otherwise the element is inserted at the appropriate position.
 * <p>
 * This method should be called (by the content provider) when a single element 
 * has been added to the model, in order to cause the viewer to accurately
 * reflect the model. This method only affects the viewer, not the model.
 * Note that there is another method for efficiently processing the simultaneous
 * addition of multiple elements.
 * </p>
 *
 * @param element the element to add
 */
public void add(Object element) {
	add(new Object[] { element });
}
/**
 * Cancels a currently active cell editor. All changes already done in the cell
 * editor are lost.
 */
public void cancelEditing() {
	tableViewerImpl.cancelEditing();
}
/* (non-Javadoc)
 * Method declared on StructuredViewer.
 */
protected Widget doFindInputItem(Object element) {
	if (equals(element, getRoot()))
		return getTable();
	return null;
}
/* (non-Javadoc)
 * Method declared on StructuredViewer.
 */
protected Widget doFindItem(Object element) {
	TableItem[] children = table.getItems();
	for (int i = 0; i < children.length; i++) {
		TableItem item = children[i];
		Object data = item.getData();
		if (data != null && equals(data, element))
			return item;
	}

	return null;
}
/* (non-Javadoc)
 * Method declared on StructuredViewer.
 */
protected void doUpdateItem(Widget widget, Object element, boolean fullMap) {
	if (widget instanceof TableItem) {
		final TableItem item = (TableItem) widget;
		
		// remember element we are showing
		if (fullMap) {
			associate(element, item);
		} else {
			item.setData(element);
			mapElement(element, item);	
		}
	
		IBaseLabelProvider prov = getLabelProvider();
		ITableLabelProvider tprov = null;
		ILabelProvider lprov = null;
		
			
		if (prov instanceof ITableLabelProvider) {
			tprov = (ITableLabelProvider) prov;
		}
		else {
			lprov = (ILabelProvider) prov;
		}
		int columnCount = table.getColumnCount();
		TableItem ti = item;
		// Also enter loop if no columns added.  See 1G9WWGZ: JFUIF:WINNT - TableViewer with 0 columns does not work
		for (int column = 0; column < columnCount || column == 0; column++) {
			// Similar code in TableTreeViewer.doUpdateItem()
			String text = "";//$NON-NLS-1$
			Image image = null;
			if (tprov != null) {
				text = tprov.getColumnText(element, column);
				image = tprov.getColumnImage(element, column);
			}
			else {
				if (column == 0) {
					if (lprov instanceof IViewerLabelProvider) {
						IViewerLabelProvider itemProvider = (IViewerLabelProvider) lprov;
						ViewerLabel updateLabel = new ViewerLabel(item.getText(),item.getImage());
					
						itemProvider.updateLabel(updateLabel, element);
						text = updateLabel.getText();
						image = updateLabel.getImage();		
					
					}
					else{
						text = lprov.getText(element);
						image = lprov.getImage(element);	
					}
		    	}
	    	}
			ti.setText(column, text);
			if (ti.getImage(column) != image) {
				ti.setImage(column, image);
			}
		}
		if (prov instanceof IColorProvider) {
			IColorProvider cprov = (IColorProvider) prov;
			ti.setForeground(cprov.getForeground(element));
			ti.setBackground(cprov.getBackground(element));
		}
		if (prov instanceof IFontProvider) {
		    IFontProvider fprov = (IFontProvider) prov;
		    ti.setFont(fprov.getFont(element));
		}		
	}
}
/**
 * Starts editing the given element.
 *
 * @param element the element
 * @param column the column number
 */
public void editElement(Object element, int column) {
	tableViewerImpl.editElement(element,column);
}
/**
 * Returns the cell editors of this table viewer.
 *
 * @return the list of cell editors
 */
public CellEditor[] getCellEditors() {
	return tableViewerImpl.getCellEditors();
}
/**
 * Returns the cell modifier of this table viewer.
 *
 * @return the cell modifier
 */
public ICellModifier getCellModifier() {
	return tableViewerImpl.getCellModifier();
}
/**
 * Returns the column properties of this table viewer.
 * The properties must correspond with the columns of the table control.
 * They are used to identify the column in a cell modifier.
 *
 * @return the list of column properties
 */
public Object[] getColumnProperties() {
	return tableViewerImpl.getColumnProperties();
}
/* (non-Javadoc)
 * Method declared on Viewer.
 */
public Control getControl() {
	return table;
}
/**
 * Returns the element with the given index from this table viewer.
 * Returns <code>null</code> if the index is out of range.
 * <p>
 * This method is internal to the framework.
 * </p>
 *
 * @param index the zero-based index
 * @return the element at the given index, or <code>null</code> if the
 *   index is out of range
 */
public Object getElementAt(int index) {
	if (index >= 0 && index < table.getItemCount()) {
		TableItem i = table.getItem(index);
		if(i != null)
		    return i.getData();
	}
	return null;
}
/**
 * The table viewer implementation of this <code>Viewer</code> framework
 * method returns the label provider, which in the case of table 
 * viewers will be an instance of either <code>ITableLabelProvider</code>
 * or <code>ILabelProvider</code>.
 * If it is an <code>ITableLabelProvider</code>, then it provides a
 * separate label text and image for each column. If it is an 
 * <code>ILabelProvider</code>, then it provides only the label text 
 * and image for the first column, and any remaining columns are blank.
 */
public IBaseLabelProvider getLabelProvider() {
	return super.getLabelProvider();
}
/* (non-Javadoc)
 * Method declared on StructuredViewer.
 */
protected List getSelectionFromWidget() {
	Widget[] items = table.getSelection();
	ArrayList list = new ArrayList(items.length);
	for (int i = 0; i < items.length; i++) {
		Widget item = items[i];
		Object e = item.getData();
		if (e != null)
			list.add(e);
	}
	return list;
}
/**
 * Returns this table viewer's table control.
 *
 * @return the table control
 */
public Table getTable() {
	return table;
}
/* (non-Javadoc)
 * Method declared on StructuredViewer.
 */
protected void hookControl(Control control) {
	super.hookControl(control);
	Table tableControl = (Table)control;
	tableControl.addMouseListener(new MouseAdapter() {
		public void mouseDown(MouseEvent e) {
			tableViewerImpl.handleMouseDown(e);
		}
	});
}
/*
 * Returns the index where the item should be inserted.
*/
protected int indexForElement(Object element) {
	ViewerSorter sorter = getSorter();
	if(sorter == null)
		return table.getItemCount();
	int count = table.getItemCount();
	int min = 0, max = count - 1;
	while (min <= max) {
		int mid = (min + max) / 2;
		Object data = table.getItem(mid).getData();
		int compare = sorter.compare(this, data, element);
		if (compare == 0) {
			// find first item > element
			while (compare == 0) {
				++mid;
				if (mid >= count) {
					break;
				}
				data = table.getItem(mid).getData();
				compare = sorter.compare(this, data, element);
			}
			return mid;
		}
		if (compare < 0)
			min = mid + 1;
		else
			max = mid - 1;
	}
	return min;
}
/**
 * Initializes the table viewer implementation.
 */
/**
 * ****************************
 * Abeer :) - this is the only diff between TableViewer and NPTableViewer
 * using NPTableViewerImpl instead of TableViewerImpl
 * ****************************
 */
private void initTableViewerImpl() {
	tableViewerImpl = new NPTableViewerImpl(this) {
		Rectangle getBounds(Item item, int columnNumber) {
			return ((TableItem)item).getBounds(columnNumber);
		}
		int getColumnCount() {
			return getTable().getColumnCount();
		}
		Item[] getSelection() {
			return getTable().getSelection();
		}
		void setEditor(Control w, Item item, int columnNumber) {
			tableEditor.setEditor(w, (TableItem)item, columnNumber);
		}
		void setSelection(StructuredSelection selection, boolean b) {
			NPTableViewer.this.setSelection(selection,b);
		}
		void showSelection() {
			getTable().showSelection();
		}
		void setLayoutData(CellEditor.LayoutData layoutData) {
			tableEditor.grabHorizontal = layoutData.grabHorizontal;
			tableEditor.horizontalAlignment = layoutData.horizontalAlignment;
			tableEditor.minimumWidth = layoutData.minimumWidth;
		}
		void handleDoubleClickEvent() {
			Viewer viewer = getViewer();
			fireDoubleClick (new DoubleClickEvent(viewer, viewer.getSelection()));
			fireOpen (new OpenEvent(viewer, viewer.getSelection()));
		}
	};
}
/* (non-Javadoc)
 * Method declared on Viewer.
 */
protected void inputChanged(Object input, Object oldInput) {
	getControl().setRedraw(false);
	try {
		// refresh() attempts to preserve selection, which we want here
		refresh();
	}
	finally {
		getControl().setRedraw(true);
	}
}
/**
 * Inserts the given element into this table viewer at the given position.
 * If this viewer has a sorter, the position is ignored and the element is inserted
 * at the correct position in the sort order.
 * <p>
 * This method should be called (by the content provider) when elements 
 * have been added to the model, in order to cause the viewer to accurately
 * reflect the model. This method only affects the viewer, not the model.
 * </p>
 *
 * @param element the element
 * @param position a 0-based position relative to the model, or -1 to indicate the last position
 */
public void insert(Object element, int position) {
	tableViewerImpl.applyEditorValue();
	if (getSorter() != null || hasFilters()) {
		add(element);
		return;
	}
	if (position == -1)
		position = table.getItemCount();
	updateItem(new TableItem(table, SWT.NONE, position), element);
}
/* (non-Javadoc)
 * Method declared on StructuredViewer.
 */
protected void internalRefresh(Object element) {
	internalRefresh(element, true);
}

/* (non-Javadoc)
 * Method declared on StructuredViewer.
 */
protected void internalRefresh(Object element, boolean updateLabels) {
	tableViewerImpl.applyEditorValue();
	if (element == null || equals(element, getRoot())) {
		// the parent

		// in the code below, it is important to do all disassociates
		// before any associates, since a later disassociate can undo an earlier associate
		// e.g. if (a, b) is replaced by (b, a), the disassociate of b to item 1 could undo
		// the associate of b to item 0.
		
		Object[] children = getSortedChildren(getRoot());
		TableItem[] items = table.getItems();
		int min = Math.min(children.length, items.length);
		for (int i = 0; i < min; ++i) {
			// if the element is unchanged, update its label if appropriate
			if (equals(children[i], items[i].getData())) {
				if (updateLabels) {
					updateItem(items[i], children[i]);
				}
				else {
					// associate the new element, even if equal to the old one,
					// to remove stale references (see bug 31314)
					associate(children[i], items[i]);
				}
			}
			else {
				// updateItem does an associate(...), which can mess up
				// the associations if the order of elements has changed.
				// E.g. (a, b) -> (b, a) first replaces a->0 with b->0, then replaces b->1 with a->1, but this actually removes b->0.
				// So, if the object associated with this item has changed,
				// just disassociate it for now, and update it below.
				items[i].setText(""); //$NON-NLS-1$
				items[i].setImage(new Image[0]);
				disassociate(items[i]);
			}
		}
		
		// dispose of all items beyond the end of the current elements
		if (min < items.length) {
			for (int i = items.length; --i >= min;) {
				disassociate(items[i]);
			}
			table.remove(min, items.length-1);
		}
		
		// Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get scrunched
		if (table.getItemCount() == 0) {
			table.removeAll();
		}

		// Update items which were removed above
		for (int i = 0; i < min; ++i) {
			if (items[i].getData() == null) {
				updateItem(items[i], children[i]);
			}
		}
	
		// add any remaining elements
		for (int i = min; i < children.length; ++i) {
			updateItem(new TableItem(table, SWT.NONE, i), children[i]);
		}
	}
	else {
		Widget w = findItem(element);
		if (w != null) {
			updateItem(w, element);
		}
	}
}
/**
 * Removes the given elements from this table viewer.
 *
 * @param elements the elements to remove
 */
private void internalRemove(final Object[] elements) {
	Object input = getInput();
	for (int i = 0; i < elements.length; ++i) {
		if (equals(elements[i], input)) {
			setInput(null);
			return;
		}
	}
	// use remove(int[]) rather than repeated TableItem.dispose() calls
	// to allow SWT to optimize multiple removals
	int[] indices = new int[elements.length];
	int count = 0;
	for (int i = 0; i < elements.length; ++i) {
		Widget w = findItem(elements[i]);
		if (w instanceof TableItem) {
			TableItem item = (TableItem) w;
			disassociate(item);
			indices[count++] = table.indexOf(item);
		}
	}
	if (count < indices.length) {
		System.arraycopy(indices, 0, indices = new int[count], 0, count);
	}
	table.remove(indices);
	
	// Workaround for 1GDGN4Q: ITPUI:WIN2000 - TableViewer icons get scrunched
	if (table.getItemCount() == 0) {
		table.removeAll();
	}
}
/**
 * Returns whether there is an active cell editor.
 *
 * @return <code>true</code> if there is an active cell editor, and 
 *   <code>false</code> otherwise
 */
public boolean isCellEditorActive() {
	return tableViewerImpl.isCellEditorActive();
}
/**
 * Removes the given elements from this table viewer.
 * The selection is updated if required.
 * <p>
 * This method should be called (by the content provider) when elements 
 * have been removed from the model, in order to cause the viewer to accurately
 * reflect the model. This method only affects the viewer, not the model.
 * </p>
 *
 * @param elements the elements to remove
 */
public void remove(final Object[] elements) {
	assertElementsNotNull(elements);
	preservingSelection(new Runnable() {
		public void run() {
			internalRemove(elements);
		}
	});
}
/**
 * Removes the given element from this table viewer.
 * The selection is updated if necessary.
 * <p>
 * This method should be called (by the content provider) when a single element 
 * has been removed from the model, in order to cause the viewer to accurately
 * reflect the model. This method only affects the viewer, not the model.
 * Note that there is another method for efficiently processing the simultaneous
 * removal of multiple elements.
 * </p>
 *
 * @param element the element
 */
public void remove(Object element) {
	remove(new Object[] { element });
}
/*
 * Non-Javadoc.
 * Method defined on StructuredViewer.
 */
public void reveal(Object element) {
	Assert.isNotNull(element);
	Widget w = findItem(element);
	if (w instanceof TableItem)
		getTable().showItem((TableItem) w);
}
/**
 * Sets the cell editors of this table viewer.
 *
 * @param editors the list of cell editors
 */
public void setCellEditors(CellEditor[] editors) {
	tableViewerImpl.setCellEditors(editors);
}
/**
 * Sets the cell modifier of this table viewer.
 *
 * @param modifier the cell modifier
 */
public void setCellModifier(INPCellModifier modifier) {
	tableViewerImpl.setCellModifier(modifier);
}
/**
 * Sets the column properties of this table viewer.
 * The properties must correspond with the columns of the table control.
 * They are used to identify the column in a cell modifier.
 *
 * @param columnProperties the list of column properties
 */
public void setColumnProperties(String[] columnProperties) {
	tableViewerImpl.setColumnProperties(columnProperties);
}
/**
 * The table viewer implementation of this <code>Viewer</code> framework
 * method ensures that the given label provider is an instance
 * of either <code>ITableLabelProvider</code> or <code>ILabelProvider</code>.
 * If it is an <code>ITableLabelProvider</code>, then it provides a
 * separate label text and image for each column. If it is an 
 * <code>ILabelProvider</code>, then it provides only the label text 
 * and image for the first column, and any remaining columns are blank.
 */
public void setLabelProvider(IBaseLabelProvider labelProvider) {
	Assert.isTrue(labelProvider instanceof ITableLabelProvider || labelProvider instanceof ILabelProvider );
	super.setLabelProvider(labelProvider);
}
/* (non-Javadoc)
 * Method declared on StructuredViewer.
 */
protected void setSelectionToWidget(List list, boolean reveal) {
	if (list == null) {
		table.deselectAll();
		return;
	}
	int size = list.size();
	TableItem[] items = new TableItem[size];
	TableItem firstItem = null;
	int count = 0;
	for (int i = 0; i < size; ++i) {
		Object o = list.get(i);
		Widget w = findItem(o);
		if (w instanceof TableItem) {
			TableItem item = (TableItem) w;
			items[count++] = item;
			if (firstItem == null)
				firstItem = item;
		}
	}
	if (count < size) {
		System.arraycopy(items, 0, items = new TableItem[count], 0, count);
	}
	table.setSelection(items);
	
	if (reveal && firstItem != null) {
		table.showItem(firstItem);
	}
}
}

package com.kenati.npeint.tools.cliide.ui.widgets;

/**
 * @author abeer
 * We are using all the code from the TableViewerImpl class as is since 
 * access to the TableViewerImpl class is restricted to its package only 
 * We are using INPCellModifier instead of ICellModifier
 * We need to change only one line of code:
 * in method activateCellEditor(), 
 * instead of "cellEditor = cellEditors[columnNumber];"
 * we are using "cellEditor = cellModifier.getCellEditor(element, property);"
 * We need maintain to this class in sync with future releases of Eclipse
 */

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ICellEditorListener;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Item;

/* package */ abstract class NPTableViewerImpl {
	
	private CellEditor cellEditor;
	private CellEditor[] cellEditors;
	private INPCellModifier cellModifier;
	private String[] columnProperties;
	private Item tableItem;
	private int columnNumber;
	private ICellEditorListener cellEditorListener;
	private FocusListener focusListener;
	private MouseListener mouseListener;
	private int doubleClickExpirationTime;
	private StructuredViewer viewer;
	

	NPTableViewerImpl(StructuredViewer viewer) {
	this.viewer = viewer;
	initCellEditorListener();
}

/**
 * Returns this <code>TableViewerImpl</code> viewer
 * 
 * @return the viewer
 */
public StructuredViewer getViewer() {
	return viewer;
}

private void activateCellEditor() {
	if(cellModifier != null) {
		Object element = tableItem.getData();
		String property = columnProperties[columnNumber];
		if (cellModifier.canModify(element, property)) {
/*****************************
 * Abeer :) - this is the only diff between TableViewerImpl and NPTableViewerImpl
 * instead of "cellEditor = cellEditors[columnNumber];"
 * we are using "cellEditor = cellModifier.getCellEditor(element, property);"
 *****************************/
			//cellEditor = cellEditors[columnNumber];
		    cellEditor = cellModifier.getCellEditor(element, property);
			//table.showSelection();
			cellEditor.addListener(cellEditorListener);
			Object value = cellModifier.getValue(element, property);
			cellEditor.setValue(value);
			// Tricky flow of control here:
			// activate() can trigger callback to cellEditorListener which will clear cellEditor
			// so must get control first, but must still call activate() even if there is no control.
			final Control control = cellEditor.getControl();
			cellEditor.activate();
			if (control == null)
				return;
	 		setLayoutData(cellEditor.getLayoutData());
			setEditor(control, tableItem, columnNumber);		
			cellEditor.setFocus();
			if(focusListener == null) {
				focusListener = new FocusAdapter() {
					public void focusLost(FocusEvent e) {
						applyEditorValue();
					}
				};
			}
			control.addFocusListener(focusListener);
			mouseListener = new MouseAdapter() {
				public void mouseDown(MouseEvent e) {
					// time wrap?	
					// check for expiration of doubleClickTime
					if (e.time <= doubleClickExpirationTime ) {
						control.removeMouseListener(mouseListener);
						cancelEditing();
						handleDoubleClickEvent();
					} else if (mouseListener != null) {
						control.removeMouseListener(mouseListener);
					}
				} 
			};				
			control.addMouseListener(mouseListener);
		}
	}	
}
/**
 * Activate a cell editor for the given mouse position.
 */
private void activateCellEditor(MouseEvent event) {
	if (tableItem == null || tableItem.isDisposed()) {
		//item no longer exists
		return;
	}
	int columnToEdit;
	int columns = getColumnCount();
	if (columns == 0) {
		// If no TableColumn, Table acts as if it has a single column
		// which takes the whole width.
		columnToEdit = 0;
	}
	else {
		columnToEdit = -1;
		for (int i = 0; i < columns; i++) {
			Rectangle bounds = getBounds(tableItem, i);
			if (bounds.contains(event.x, event.y)) {
				columnToEdit = i;
				break;
			}
		}
		if (columnToEdit == -1) {
			return;
		}
	}
	
	columnNumber = columnToEdit;
	activateCellEditor();
}
/**
 * Deactivates the currently active cell editor.
 */
public void applyEditorValue() {
	CellEditor c = this.cellEditor;
	if (c != null) {
		// null out cell editor before calling save
		// in case save results in applyEditorValue being re-entered
		// see 1GAHI8Z: ITPUI:ALL - How to code event notification when using cell editor ?
		this.cellEditor = null;
		Item t = this.tableItem;
		// don't null out table item -- same item is still selected
		if (t != null && !t.isDisposed()) {
			saveEditorValue(c, t);
		}
		setEditor(null, null, 0);
		c.removeListener(cellEditorListener);
		Control control = c.getControl();
		if (control != null) {
			if (mouseListener != null) {
				control.removeMouseListener(mouseListener);
			}
			if (focusListener != null) {
				control.removeFocusListener(focusListener);
			}
		}
		c.deactivate();
	}
}
/**
 * Cancels the active cell editor, without saving the value 
 * back to the domain model.
 */
public void cancelEditing() {
	if (cellEditor != null) {
		setEditor(null, null, 0);
		cellEditor.removeListener(cellEditorListener);
		CellEditor oldEditor = cellEditor;
		cellEditor = null;
		oldEditor.deactivate();
	}
}
/**
 * Start editing the given element. 
 */
public void editElement(Object element, int column) {
	if (cellEditor != null)
		applyEditorValue();

	setSelection(new StructuredSelection(element), true);
	Item[] selection = getSelection();
	if (selection.length != 1)
		return;

	tableItem = selection[0];

	// Make sure selection is visible
	showSelection();
	columnNumber = column;
	activateCellEditor();

}
abstract Rectangle getBounds(Item item, int columnNumber);
public CellEditor[] getCellEditors() {
	return cellEditors;
}
public ICellModifier getCellModifier() {
	return cellModifier; 
}
abstract int getColumnCount();
public Object[] getColumnProperties() {
	return columnProperties;
}
abstract Item[] getSelection();
/**
 * Handles the mouse down event; activates the cell editor.
 */
public void handleMouseDown(MouseEvent event) {
	if (event.button != 1)
		return;

	if (cellEditor != null)
		applyEditorValue();
	
	// activate the cell editor immediately.  If a second mouseDown
	// is received prior to the expiration of the doubleClick time then
	// the cell editor will be deactivated and a doubleClick event will
	// be processed.
	//
	doubleClickExpirationTime = event.time + Display.getCurrent().getDoubleClickTime();								

	Item[] items = getSelection();
	// Do not edit if more than one row is selected.
	if (items.length != 1) {
		tableItem = null;
		return;
	}
	tableItem = items[0];
	activateCellEditor(event);
}
private void initCellEditorListener() {
	cellEditorListener = new ICellEditorListener() {
		public void editorValueChanged(boolean oldValidState, boolean newValidState) {
			// Ignore.
		}
		
		public void cancelEditor() {
			NPTableViewerImpl.this.cancelEditing();
		}
		
		public void applyEditorValue() {
			NPTableViewerImpl.this.applyEditorValue();
		}
	};
}
/**
 * Returns <code>true</code> if there is an active cell editor; otherwise
 * <code>false</code> is returned.
 */
public boolean isCellEditorActive() {
	return cellEditor != null;
}
/**
 * Saves the value of the currently active cell editor,
 * by delegating to the cell modifier.
 */
private void saveEditorValue(CellEditor cellEditor, Item tableItem) {
	if (cellModifier != null) {
		if (!cellEditor.isValueValid()) {
			///Do what ???
		}
		String property = null;
		if (columnProperties != null && columnNumber < columnProperties.length)
			property = columnProperties[columnNumber];
		cellModifier.modify(tableItem, property, cellEditor.getValue());
	}
}
public void setCellEditors(CellEditor[] editors) {
	this.cellEditors = editors;
}
public void setCellModifier(INPCellModifier modifier) {
	this.cellModifier = modifier; 
}
public void setColumnProperties(String[] columnProperties) {
	this.columnProperties = columnProperties;
}
abstract void setEditor(Control w, Item item, int fColumnNumber);
abstract void setLayoutData(CellEditor.LayoutData layoutData);
abstract void setSelection(StructuredSelection selection, boolean b);
abstract void showSelection();
abstract void handleDoubleClickEvent();
}

package com.kenati.npeint.tools.cliide.ui.dialogs;

import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.ComboBoxCellEditor;
import org.eclipse.jface.viewers.TextCellEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.TableItem;

import com.kenati.npeint.tools.cliide.ui.model.PromptElementList;
import com.kenati.npeint.tools.cliide.ui.model.PromptElementTO;
import com.kenati.npeint.tools.cliide.ui.widgets.INPCellModifier;
import com.kenati.npeint.tools.cliide.ui.widgets.NPTableViewer;
import com.kenati.npeint.tools.common.Constants;

public class PromptElementCellModifier implements INPCellModifier 
{
	private NPTableViewer tableViewer;
	private PromptElementList elementList;	
//	 column indices
	private static final int COL_TYPE = 0;
	private static final int COL_VALUE = 1;

//	column 1 : type (Combo box)
	private ComboBoxCellEditor typeEditor;
	private String noItems[] = {""};
//	column 2 : value - use prompt names (Combo box)
	private ComboBoxCellEditor usePromptNameEditor;
//OR
//  column 2 : value - string / function name (Free text)
	private TextCellEditor stringValueEditor;

	PromptElementCellModifier(NPTableViewer tableViewer)
	{
		this.tableViewer = tableViewer;
//		column 1 : filter criterias (Combo box)		
		typeEditor = new ComboBoxCellEditor(tableViewer.getTable(), noItems, SWT.READ_ONLY);
//		column 2 : value - use prompt names (Combo box)		
		usePromptNameEditor = new ComboBoxCellEditor(tableViewer.getTable(), noItems, SWT.READ_ONLY);
//		 Column 2 : string / function name (Free text)
		stringValueEditor = new TextCellEditor(tableViewer.getTable());	
	}
	
	public void setParamList(PromptElementList elementList)
	{
		this.elementList = elementList;
	}
	
	public CellEditor getCellEditor(Object element, String property)
	{
//		get the index of the column
		int colIndex = -1;
		for(int i=0; i<tableViewer.getTable().getColumnCount(); i++)
			if (property.equalsIgnoreCase(tableViewer.getTable().getColumn(i).getText()))
			{
				colIndex = i;
				break;
			}
		
		switch(colIndex)
		{
			case COL_TYPE:
			    if (noItems != null)
			    {
				    typeEditor.setItems(elementList.getTypes());
				    noItems = null;
			    }
			    return typeEditor;
			case COL_VALUE:
			    PromptElementTO elementTO = (PromptElementTO) element;
			    if (elementTO.getType().equals(Constants.ATTR_PROMPTELEMENT_STRING) || elementTO.getType().equals(Constants.ATTR_PROMPTELEMENT_FUNCTION))
			        return stringValueEditor;
			    else if (elementTO.getType().equals(Constants.ATTR_PROMPTELEMENT_USEPROMPT))
			    {
			        String[] usePromptNames  = elementList.getUsePromptNames();
			        if (usePromptNames != null)
			            usePromptNameEditor.setItems(usePromptNames);
			        return usePromptNameEditor;			        
			    }
			 default:
			     return null;
		}
	}

	public boolean canModify(Object element, String property)
	{
		//get the selected TO obj
		PromptElementTO selElement = (PromptElementTO) element;
					
		//check if the element is a new or saved one
		if (selElement.isNew())
			return true;
		else
		{
//			get the index of the column
			int colIndex = -1;
			for(int i=0; i<tableViewer.getTable().getColumnCount(); i++)
				if (property.equalsIgnoreCase(tableViewer.getTable().getColumn(i).getText()))
				{
					colIndex = i;
					break;
				}
			//can only change the value but not the type
			if (colIndex == 1)
				return true;
			else
				return false;
		}
	}

	public Object getValue(Object element, String property)
	{
//		get the index of the column
		int colIndex = -1;
		for(int i=0; i<tableViewer.getTable().getColumnCount(); i++)
			if (property.equalsIgnoreCase(tableViewer.getTable().getColumn(i).getText()))
			{
				colIndex = i;
				break;
			}

		Object result = null;
		PromptElementTO elementTO = (PromptElementTO) element;

		switch(colIndex)
		{
			case COL_VALUE: //value
			    if (elementTO.getType() == Constants.ATTR_PROMPTELEMENT_USEPROMPT)
			    {
//			    	get the current use prompt name
			        String promptName = elementTO.getValue();
//			    	get the list of all prompt names in the combo box
			        String[] usePromptNames  = elementList.getUsePromptNames();
			        if (usePromptNames == null)
			            result = "";
			        else
			        {
						//get the index of the current use prompt
						int index = 0;
						for (index=0; index<usePromptNames.length; index++)
						{
							if (usePromptNames[index].equals(promptName))
								break;
						}
						if (index == usePromptNames.length)
							index = 0;
						result = new Integer(index);			
			        }
			    }
			    else
			    {
					result = elementTO.getValue();
					if (result == null)
						result = "";
			    }
				break;
			case COL_TYPE: //type
				//get the current type
				String type = elementTO.getType();
				//get the list of all criterias in the combo box
				String[] allTypes = elementList.getTypes();
				//get the index of the current criteria
				int index = 0;
				for (index=0; index<allTypes.length; index++)
				{
					if (allTypes[index].equals(type))
						break;
				}
				if (index == allTypes.length)
					index = 0;
				result = new Integer(index);			
				break;
		}
		
		return result;
	}
	
	public void modify(Object element, String property, Object value)
	{
		//get the index of the column
		int colIndex = -1;
		for(int i=0; i<tableViewer.getTable().getColumnCount(); i++)
			if (property.equalsIgnoreCase(tableViewer.getTable().getColumn(i).getText()))
			{
				colIndex = i;
				break;
			}
		
		TableItem item = (TableItem) element;
		PromptElementTO elementTO = (PromptElementTO) item.getData();
		
		boolean isColWidthValid = true;
		switch(colIndex)
		{
			case COL_TYPE: //type
				if (((Integer)value).intValue() == -1)
					elementTO.setType("");
				else
					elementTO.setType((String) elementList.getTypes()[((Integer)value).intValue()]);
				elementTO.setValue("");
				break;
			case COL_VALUE: //value
			    if (elementTO.getType() == Constants.ATTR_PROMPTELEMENT_USEPROMPT)
			    {
					if (((Integer)value).intValue() == -1)
					    elementTO.setValue("");
					else
					    elementTO.setValue((String) elementList.getUsePromptNames()[((Integer)value).intValue()]);
			    }
			    else
			    {
			        elementTO.setValue(((String)value).trim());
			    }
		}
		((PromptElementList) tableViewer.getInput()).elementChanged(elementTO);
	}
}


Back to the top