/*******************************************************************************
* Copyright (c) 2000, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
* Jeremy Dowdall <jeremyd@aspencloud.com>
* - modified for use with org.aspencloud.widgets.CTableTree
*******************************************************************************/
package org.eclipse.swt.nebula.nebface.ctabletreeviewer.ccontainerviewer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.eclipse.jface.util.Assert;
import org.eclipse.jface.viewers.CellEditor;
import org.eclipse.jface.viewers.IBaseLabelProvider;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ILazyContentProvider;
import org.eclipse.jface.viewers.ITableColorProvider;
import org.eclipse.jface.viewers.ITableLabelProvider;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.nebula.widgets.ctabletree.CContainer;
import org.eclipse.swt.nebula.widgets.ctabletree.CContainerCell;
import org.eclipse.swt.nebula.widgets.ctabletree.CContainerItem;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Widget;
/**
* <p>
* NOTE: THIS VIEWER AND ITS API ARE STILL UNDER DEVELOPMENT. THIS IS A PRE-RELEASE ALPHA
* VERSION. USERS SHOULD EXPECT API CHANGES IN FUTURE VERSIONS.
* </p>
*/
public abstract class CContainerViewer extends StructuredViewer {
protected CContainer container;
protected ICContainerCellProvider cellProvider;
protected String[] columnProperties;
/**
* Creates a container viewer on the given container control.
* The viewer has no input, no content provider, a default label provider,
* no sorter, and no filters.
*
* @param container the Container control
*/
public CContainerViewer(CContainer container) {
this.container = container;
hookControl(container);
}
/**
* Adds the given element to this list 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
*/
public void add(Object element) {
add(new Object[] { element });
}
/**
* 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);
createItem(element, index);
}
}
/**
* Convenience method; use when using CTable as a List
* @see org.aspencloud.viewers.CCalendarViewer#setCellProvider(Class[])
*/
public void setCellProvider(Class clazz) {
final Class[] classes = new Class[] { clazz };
this.cellProvider = new ICContainerCellProvider() {
public Class[] getCellClasses(Object element) {
return classes;
}
};
}
/**
*
* @param cellClasses
*/
public void setCellProvider(Class[] cellClasses) {
final Class[] classes = cellClasses;
this.cellProvider = new ICContainerCellProvider() {
public Class[] getCellClasses(Object element) {
return classes;
}
};
}
public void setCellProvider(ICContainerCellProvider provider) {
this.cellProvider = provider;
}
/**
*
* @param element
* @param index
*/
protected abstract void createItem(Object element, int index);
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
*/
protected Widget doFindInputItem(Object element) {
if (equals(element, getRoot()))
return getContainer();
return null;
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
*/
protected Widget doFindItem(Object element) {
CContainerItem[] children = container.getItems();
for(int i = 0; i < children.length; i++) {
CContainerItem item = children[i];
Object data = item.getData();
if(data != null && equals(data, element))
return item;
}
return null;
}
protected void associate(Object element, Widget item) {
Object data = item.getData();
if (data != element) {
if (data != null) {
disassociate(item);
}
item.setData(element);
}
// Always map the element, even if data == element,
// since unmapAllElements() can leave the map inconsistent
// See bug 2741 for details.
mapElement(element, item);
}
protected void doUpdateCell(int index, CContainerCell cell, Object element, String[] properties) {
IBaseLabelProvider prov = (IBaseLabelProvider) getLabelProvider();
if(prov != null) {
Color background = null;
Color foreground = null;
if(prov instanceof ITableColorProvider) {
background = ((ITableColorProvider) prov).getBackground(element, index);
foreground = ((ITableColorProvider) prov).getForeground(element, index);
} else if(prov instanceof IColorProvider) {
background = ((IColorProvider) prov).getBackground(element);
foreground = ((IColorProvider) prov).getForeground(element);
}
if(background != null) cell.setCellBackground(background);
if(foreground != null) cell.setCellForeground(foreground);
// TODO doUpdateItem - set fonts...
}
}
protected void doUpdateItem(Widget widget, Object element, boolean fullMap) {
if(widget instanceof CContainerItem) {
final CContainerItem item = (CContainerItem) widget;
// remember element we are showing
if (fullMap) {
associate(element, item);
} else {
item.setData(element);
mapElement(element, item);
}
CContainerCell[] cells = item.getCells();
for(int i = 0; i < cells.length; i++) {
CContainerCell cell = (CContainerCell) cells[i];
if(cell.update(element, getStringColumnProperties())) {
doUpdateCell(i, cell, element, getStringColumnProperties());
}
}
}
}
public CContainer getContainer() {
return container;
}
/**
* 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 columnProperties;
}
public String[] getStringColumnProperties() {
return columnProperties;
}
/* (non-Javadoc)
* Method declared on Viewer.
*/
public Control getControl() {
return container;
}
/**
* 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 < container.getItemCount()) {
CContainerItem i = container.getItem(index);
if(i != null)
return i.getData();
}
return null;
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
*/
protected List getSelectionFromWidget() {
Widget[] items = container.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 the index where the item should be inserted.
*/
protected int indexForElement(Object element) {
ViewerSorter sorter = getSorter();
if (sorter == null)
return container.getItemCount();
int count = container.getItemCount();
int min = 0, max = count - 1;
while(min <= max) {
int mid = (min + max) / 2;
Object data = container.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 = container.getItem(mid).getData();
compare = sorter.compare(this, data, element);
}
return mid;
}
if(compare < 0) {
min = mid + 1;
} else {
max = mid - 1;
}
}
return min;
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, java.lang.Object)
*/
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) {
if (getSorter() != null || hasFilters()) {
add(element);
return;
}
if (position == -1)
position = container.getItemCount();
createItem(element,position);
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
*/
protected void internalRefresh(Object element) {
internalRefresh(element, true);
}
/*
* (non-Javadoc)
* @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object, boolean)
*/
protected void internalRefresh(Object element, boolean updateLabels) {
if (element == null || equals(element, getRoot())) {
internalRefreshAll(updateLabels);
} else {
Widget w = findItem(element);
if (w != null) {
updateItem(w, element);
}
}
}
/**
* Refresh all of the elements of the table. update the
* labels if updatLabels is true;
* @param updateLabels
*
* @since 3.1
*/
protected void internalRefreshAll(boolean updateLabels) {
Object[] children = getSortedChildren(getRoot());
CContainerItem[] items = getContainer().getItems();
List ci = Arrays.asList(children);
List li = Arrays.asList(items);
for(int i = 0; i < items.length; i++) {
if(!items[i].isDisposed()) {
Object data = items[i].getData();
if(!ci.contains(items[i].getData())) {
if(data != null) {
disassociate(items[i]);
}
items[i].dispose();
}
}
}
for(int i = 0; i < children.length; i++) {
Object item = findItem(children[i]);
if(item == null) { // item does not exist for the child, insert a new one into the list
insert(children[i], i);
} else if(li.contains(item)) {
if(item != container.getItem(i)) {
container.move((CContainerItem) item, i);
}
if(updateLabels) {
refresh(children[i], true);
}
}
}
}
/**
* 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;
}
}
List l = new ArrayList();
for(int i = 0; i < elements.length; ++i) {
Widget w = findItem(elements[i]);
if(w instanceof CContainerItem) {
CContainerItem item = (CContainerItem) w;
disassociate(item);
l.add(item);
}
}
CContainerItem[] ia = l.isEmpty() ? new CContainerItem[0] : (CContainerItem[]) l.toArray(new CContainerItem[l.size()]);
container.remove(ia);
}
protected void disassociate(Widget item) {
Object element = item.getData();
Assert.isNotNull(element);
//Clear the map before we clear the data
unmapElement(element, item);
item.setData(null);
}
/**
* 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>
* <strong>NOTE:</strong> removing an object from a virtual
* table will decrement the itemCount.
*
* @param element
* the element
*/
public void remove(Object element) {
remove(new Object[] { element });
}
/**
* 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);
if (elements.length == 0) {
return;
}
preservingSelection(new Runnable() {
public void run() {
internalRemove(elements);
}
});
}
/**
* Replace the entries starting at index with elements.
* This method assumes all of these values are correct
* and will not call the content provider to verify.
* <strong>Note that this method will create a TableItem
* for all of the elements provided</strong>.
* @param element
* @param index
* @see ILazyContentProvider
*
* @since 3.1
*/
public void replace(Object element, int index){
CContainerItem item = container.getItem(index);
refreshItem(item, element);
}
/**
* @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object)
*/
public void reveal(Object element) {
Assert.isNotNull(element);
Widget w = findItem(element);
if(w instanceof CContainerItem) {
container.showItem((CContainerItem) w);
}
}
/**
* 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) {
this.columnProperties = 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);
}
/**
* @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List, boolean)
*/
protected void setSelectionToWidget(List list, boolean reveal) {
if(list == null) {
container.deselectAll();
return;
}
int size = list.size();
CContainerItem[] items = new CContainerItem[size];
int count = 0;
for(int i = 0; i < size; ++i) {
Object o = list.get(i);
Widget w = findItem(o);
if(w instanceof CContainerItem) {
CContainerItem item = (CContainerItem) w;
items[count++] = item;
}
}
if (count < size) {
System.arraycopy(items, 0, items = new CContainerItem[count], 0, count);
}
container.setSelection(items);
if(reveal) {
container.showSelection();
}
}
/**
* not yet implemented
* <p>post a feature request if you need it enabled</p>
*/
public void cancelEditing() {
// TODO Auto-generated method stub
}
/**
* not yet implemented
* <p>post a feature request if you need it enabled</p>
*/
public void editElement(Object element, int column) {
// TODO Auto-generated method stub
}
/**
* @return the Cell Provider, if there is one, null otherwise
*/
public ICContainerCellProvider getCellProvider() {
return cellProvider;
}
/**
* not yet implemented
* <p>post a feature request if you need it enabled</p>
*/
public CellEditor[] getCellEditors() {
// TODO Auto-generated method stub
return null;
}
/**
* not yet implemented
* <p>post a feature request if you need it enabled</p>
*/
public ICellModifier getCellModifier() {
// TODO Auto-generated method stub
return null;
}
/**
* not yet implemented
* <p>post a feature request if you need it enabled</p>
*/
public boolean isCellEditorActive() {
// TODO Auto-generated method stub
return false;
}
/**
* not yet implemented
* <p>post a feature request if you need it enabled</p>
*/
public void setCellEditors(CellEditor[] editors) {
// TODO Auto-generated method stub
}
/**
* not yet implemented
* <p>post a feature request if you need it enabled</p>
*/
public void setCellModifier(ICellModifier modifier) {
// TODO Auto-generated method stub
}
}