blob: 0433cb44da9af7b1ba99ec296577da073dc0955e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018, 2020 Dirk Fauth and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation
*******************************************************************************/
package org.eclipse.nebula.widgets.nattable.examples._600_GlazedLists._604_Tree;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
import org.eclipse.nebula.widgets.nattable.data.IRowDataProvider;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.dataset.car.CarService;
import org.eclipse.nebula.widgets.nattable.examples.AbstractNatExample;
import org.eclipse.nebula.widgets.nattable.examples.runner.StandaloneNatExampleRunner;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.hierarchical.HierarchicalWrapperTreeFormat;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.tree.GlazedListTreeData;
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.tree.GlazedListTreeRowModel;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
import org.eclipse.nebula.widgets.nattable.hierarchical.HierarchicalHelper;
import org.eclipse.nebula.widgets.nattable.hierarchical.HierarchicalReflectiveColumnPropertyAccessor;
import org.eclipse.nebula.widgets.nattable.hierarchical.HierarchicalWrapper;
import org.eclipse.nebula.widgets.nattable.layer.AbstractLayerTransform;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.LabelStack;
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
import org.eclipse.nebula.widgets.nattable.tree.ITreeRowModel;
import org.eclipse.nebula.widgets.nattable.tree.TreeLayer;
import org.eclipse.nebula.widgets.nattable.tree.config.DefaultTreeLayerConfiguration;
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.SortedList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.TreeList;
/**
* Example showing the TreeLayer with levels over multiple columns.
*/
public class _6043_HierarchicalTreeExample extends AbstractNatExample {
public static void main(String[] args) throws Exception {
StandaloneNatExampleRunner.run(800, 400, new _6043_HierarchicalTreeExample());
}
@Override
public String getDescription() {
return "This example shows the usage of the TreeLayer with showing nodes in multiple columns.";
}
@Override
public Control createExampleControl(Composite parent) {
// mapping from property to label, needed for column header labels
Map<String, String> propertyToLabelMap = new HashMap<>();
propertyToLabelMap.put("manufacturer", "Manufacturer");
propertyToLabelMap.put("model", "Model");
propertyToLabelMap.put("motors.identifier", "Identifier");
propertyToLabelMap.put("motors.capacity", "Capacity");
propertyToLabelMap.put("motors.capacityUnit", "Capacity Unit");
propertyToLabelMap.put("motors.maximumSpeed", "Maximum Speed");
propertyToLabelMap.put("motors.feedbacks.creationTime", "Creation Time");
propertyToLabelMap.put("motors.feedbacks.classification", "Classification");
propertyToLabelMap.put("motors.feedbacks.comment", "Comment");
BodyLayerStack bodyLayerStack = new BodyLayerStack(
CarService.getInput(),
CarService.getPropertyNames(),
new HierarchicalWrapperTreeFormat(CarService.getPropertyNames()));
// create the column header layer stack
IDataProvider columnHeaderDataProvider =
new DefaultColumnHeaderDataProvider(CarService.getPropertyNames(), propertyToLabelMap);
ILayer columnHeaderLayer = new ColumnHeaderLayer(
new DataLayer(columnHeaderDataProvider),
bodyLayerStack,
bodyLayerStack.getSelectionLayer());
// create the row header layer stack
IDataProvider rowHeaderDataProvider =
new DefaultRowHeaderDataProvider(bodyLayerStack.getBodyDataProvider());
ILayer rowHeaderLayer = new RowHeaderLayer(
new DataLayer(rowHeaderDataProvider, 40, 20),
bodyLayerStack,
bodyLayerStack.getSelectionLayer());
// create the corner layer stack
ILayer cornerLayer = new CornerLayer(
new DataLayer(
new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider)),
rowHeaderLayer,
columnHeaderLayer);
// create the grid layer composed with the prior created layer stacks
GridLayer gridLayer =
new GridLayer(bodyLayerStack, columnHeaderLayer, rowHeaderLayer, cornerLayer);
NatTable natTable = new NatTable(parent, gridLayer);
return natTable;
}
/**
* Always encapsulate the body layer stack in an AbstractLayerTransform to
* ensure that the index transformations are performed in later commands.
*/
class BodyLayerStack extends AbstractLayerTransform {
private final TreeList<HierarchicalWrapper> treeList;
private final IRowDataProvider<HierarchicalWrapper> bodyDataProvider;
private final SelectionLayer selectionLayer;
private final TreeLayer treeLayer;
@SuppressWarnings("unchecked")
public BodyLayerStack(List<?> values,
String[] propertyNames,
TreeList.Format<HierarchicalWrapper> treeFormat) {
// de-normalize with parent structure objects
List<HierarchicalWrapper> data = HierarchicalHelper.deNormalize(values, true, propertyNames);
EventList<HierarchicalWrapper> eventList = GlazedLists.eventList(data);
TransformedList<HierarchicalWrapper, HierarchicalWrapper> rowObjectsGlazedList = GlazedLists.threadSafeList(eventList);
// use the SortedList constructor with 'null' for the Comparator
// because the Comparator will be set by configuration
SortedList<HierarchicalWrapper> sortedList = new SortedList<>(rowObjectsGlazedList, null);
// wrap the SortedList with the TreeList
this.treeList = new TreeList<HierarchicalWrapper>(sortedList, treeFormat, TreeList.NODES_START_EXPANDED);
HierarchicalReflectiveColumnPropertyAccessor columnPropertyAccessor =
new HierarchicalReflectiveColumnPropertyAccessor(propertyNames);
this.bodyDataProvider = new ListDataProvider<>(this.treeList, columnPropertyAccessor);
DataLayer bodyDataLayer = new DataLayer(this.bodyDataProvider);
// simply apply labels for every column by index
bodyDataLayer.setConfigLabelAccumulator(new ColumnLabelAccumulator());
// layer for event handling of GlazedLists and PropertyChanges
GlazedListsEventLayer<HierarchicalWrapper> glazedListsEventLayer = new GlazedListsEventLayer<>(bodyDataLayer, this.treeList);
GlazedListTreeData<HierarchicalWrapper> treeData = new GlazedListTreeData<>(this.treeList);
ITreeRowModel<HierarchicalWrapper> treeRowModel = new GlazedListTreeRowModel<>(treeData);
this.selectionLayer = new SelectionLayer(glazedListsEventLayer);
this.treeLayer = new TreeLayer(this.selectionLayer, treeRowModel) {
@Override
protected boolean isTreeColumn(int columnPosition) {
// for this example we know where we show a tree node, for a
// more dynamic implementation check the
// HierarchicalTreeLayer
return columnPosition == 0 || columnPosition == 2;
}
@Override
public Object getDataValueByPosition(int columnPosition, int rowPosition) {
int columnIndex = getColumnIndexByPosition(columnPosition);
int rowIndex = getRowIndexByPosition(rowPosition);
HierarchicalWrapper rowObject = BodyLayerStack.this.bodyDataProvider.getRowObject(rowIndex);
String columnProperty = columnPropertyAccessor.getColumnProperty(columnIndex);
// inspect the level of the property
int nextLevel = columnProperty.split("\\.").length;
// get level object of the next level in the object
if ((nextLevel < rowObject.getLevels() && rowObject.getObject(nextLevel) == null) || nextLevel == rowObject.getLevels()) {
return super.getDataValueByPosition(columnPosition, rowPosition);
}
return null;
}
@Override
public LabelStack getConfigLabelsByPosition(int columnPosition, int rowPosition) {
LabelStack configLabels = super.getConfigLabelsByPosition(columnPosition, rowPosition);
// remove the labels that might be added by the super
// implementation
configLabels.removeLabel(TREE_COLUMN_CELL);
configLabels.removeLabel(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE);
configLabels.removeLabel(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE);
configLabels.removeLabel(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE);
// now add the labels from this layer
if (isTreeColumn(columnPosition) && getDataValueByPosition(columnPosition, rowPosition) != null) {
configLabels.addLabelOnTop(TREE_COLUMN_CELL);
int rowIndex = getRowIndexByPosition(rowPosition);
configLabels.addLabelOnTop(
DefaultTreeLayerConfiguration.TREE_DEPTH_CONFIG_TYPE + getModel().depth(rowIndex));
if (!getModel().hasChildren(rowIndex)) {
configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_LEAF_CONFIG_TYPE);
} else {
if (getModel().isCollapsed(rowIndex)) {
configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_COLLAPSED_CONFIG_TYPE);
} else {
configLabels.addLabelOnTop(DefaultTreeLayerConfiguration.TREE_EXPANDED_CONFIG_TYPE);
}
}
}
return configLabels;
}
};
ViewportLayer viewportLayer = new ViewportLayer(this.treeLayer);
setUnderlyingLayer(viewportLayer);
}
public SelectionLayer getSelectionLayer() {
return this.selectionLayer;
}
public TreeLayer getTreeLayer() {
return this.treeLayer;
}
public TreeList<HierarchicalWrapper> getTreeList() {
return this.treeList;
}
public IDataProvider getBodyDataProvider() {
return this.bodyDataProvider;
}
}
}