blob: 3dbda8f6f95b84656302b67418e29d959d78a951 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2012, 2013 Original authors 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:
* Original authors and others - initial API and implementation
******************************************************************************/
package org.eclipse.nebula.widgets.nattable.examples.examples;
import static org.eclipse.nebula.widgets.nattable.util.ObjectUtils.isNotNull;
import java.beans.PropertyChangeListener;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.eclipse.nebula.widgets.nattable.NatTable;
import org.eclipse.nebula.widgets.nattable.blink.BlinkConfigAttributes;
import org.eclipse.nebula.widgets.nattable.blink.BlinkingCellResolver;
import org.eclipse.nebula.widgets.nattable.blink.IBlinkingCellResolver;
import org.eclipse.nebula.widgets.nattable.columnChooser.command.DisplayColumnChooserCommandHandler;
import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
import org.eclipse.nebula.widgets.nattable.config.IConfiguration;
import org.eclipse.nebula.widgets.nattable.data.IRowDataProvider;
import org.eclipse.nebula.widgets.nattable.data.IRowIdAccessor;
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
import org.eclipse.nebula.widgets.nattable.dataset.fixture.data.BlinkingRowDataFixture;
import org.eclipse.nebula.widgets.nattable.dataset.fixture.data.RowDataListFixture;
import org.eclipse.nebula.widgets.nattable.examples.AbstractNatExample;
import org.eclipse.nebula.widgets.nattable.examples.PersistentNatExampleWrapper;
import org.eclipse.nebula.widgets.nattable.examples.examples._110_Editing.EditableGridExample;
import org.eclipse.nebula.widgets.nattable.examples.examples._131_Filtering.FilterRowGridExample;
import org.eclipse.nebula.widgets.nattable.examples.fixtures.FullFeaturedBodyLayerStack;
import org.eclipse.nebula.widgets.nattable.examples.fixtures.FullFeaturedColumnHeaderLayerStack;
import org.eclipse.nebula.widgets.nattable.examples.runner.StandaloneNatExampleRunner;
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.data.DefaultSummaryRowHeaderDataProvider;
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.DefaultRowHeaderDataLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
import org.eclipse.nebula.widgets.nattable.group.ColumnGroupModel;
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator;
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
import org.eclipse.nebula.widgets.nattable.style.CellStyleAttributes;
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
import org.eclipse.nebula.widgets.nattable.style.Style;
import org.eclipse.nebula.widgets.nattable.summaryrow.DefaultSummaryRowConfiguration;
import org.eclipse.nebula.widgets.nattable.summaryrow.ISummaryProvider;
import org.eclipse.nebula.widgets.nattable.summaryrow.SummaryRowConfigAttributes;
import org.eclipse.nebula.widgets.nattable.summaryrow.SummaryRowLayer;
import org.eclipse.nebula.widgets.nattable.summaryrow.SummationSummaryProvider;
import org.eclipse.nebula.widgets.nattable.ui.menu.HeaderMenuConfiguration;
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
import org.eclipse.nebula.widgets.nattable.util.GUIHelper;
import org.eclipse.nebula.widgets.nattable.widget.WaitDialog;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.BusyIndicator;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.ObservableElementList;
import ca.odell.glazedlists.SortedList;
public class _900_Everything_but_the_kitchen_sink extends AbstractNatExample {
public static void main(String[] args) {
StandaloneNatExampleRunner.run(1000, 800,
new PersistentNatExampleWrapper(
new _900_Everything_but_the_kitchen_sink()));
}
@Override
public String getDescription() {
return "The example with everything. Well, not quite everything, but a lot.";
}
private static final String BLINK_UP_CONFIG_LABEL = "blinkUpConfigLabel";
private static final String BLINK_DOWN_CONFIG_LABEL = "blinkDownConfigLabel";
private EventList<BlinkingRowDataFixture> baseEventList;
private PropertyChangeListener propertyChangeListener;
private ListDataProvider<BlinkingRowDataFixture> bodyDataProvider;
private NatTable natTable;
private ScheduledExecutorService scheduledThreadPool;
@Override
public Control createExampleControl(Composite parent) {
final String[] propertyNames = RowDataListFixture.getPropertyNames();
final Map<String, String> propertyToLabelMap = RowDataListFixture
.getPropertyToLabelMap();
ConfigRegistry configRegistry = new ConfigRegistry();
ColumnGroupModel columnGroupModel = new ColumnGroupModel();
// Body
LinkedList<BlinkingRowDataFixture> rowData = new LinkedList<BlinkingRowDataFixture>();
this.baseEventList = GlazedLists.threadSafeList(GlazedLists
.eventList(rowData));
ObservableElementList<BlinkingRowDataFixture> observableElementList = new ObservableElementList<BlinkingRowDataFixture>(
this.baseEventList,
GlazedLists.beanConnector(BlinkingRowDataFixture.class));
FilterList<BlinkingRowDataFixture> filterList = new FilterList<BlinkingRowDataFixture>(
observableElementList);
SortedList<BlinkingRowDataFixture> sortedList = new SortedList<BlinkingRowDataFixture>(
filterList, null);
FullFeaturedBodyLayerStack<BlinkingRowDataFixture> bodyLayer =
new FullFeaturedBodyLayerStack<BlinkingRowDataFixture>(
sortedList,
new IRowIdAccessor<BlinkingRowDataFixture>() {
@Override
public Serializable getRowId(BlinkingRowDataFixture rowObject) {
return rowObject.getSecurity_description();
}
},
propertyNames, configRegistry, columnGroupModel);
this.bodyDataProvider = bodyLayer.getBodyDataProvider();
this.propertyChangeListener = bodyLayer.getGlazedListEventsLayer();
// blinking
registerBlinkingConfigCells(configRegistry);
// Column header
FullFeaturedColumnHeaderLayerStack<BlinkingRowDataFixture> columnHeaderLayer = new FullFeaturedColumnHeaderLayerStack<BlinkingRowDataFixture>(
sortedList, filterList, propertyNames, propertyToLabelMap,
bodyLayer, bodyLayer.getSelectionLayer(), columnGroupModel,
configRegistry);
// column groups
setUpColumnGroups(columnHeaderLayer);
// Row header
final DefaultRowHeaderDataProvider rowHeaderDataProvider = new DefaultSummaryRowHeaderDataProvider(
this.bodyDataProvider);
DefaultRowHeaderDataLayer rowHeaderDataLayer = new DefaultRowHeaderDataLayer(
rowHeaderDataProvider);
rowHeaderDataLayer.setDefaultColumnWidth(50);
ILayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer,
bodyLayer, bodyLayer.getSelectionLayer());
// Corner
final DefaultCornerDataProvider cornerDataProvider = new DefaultCornerDataProvider(
columnHeaderLayer.getColumnHeaderDataProvider(),
rowHeaderDataProvider);
DataLayer cornerDataLayer = new DataLayer(cornerDataProvider);
ILayer cornerLayer = new CornerLayer(cornerDataLayer, rowHeaderLayer,
columnHeaderLayer);
// Grid
GridLayer gridLayer = new GridLayer(bodyLayer, columnHeaderLayer,
rowHeaderLayer, cornerLayer);
this.natTable = new NatTable(parent, gridLayer, false);
this.natTable.setConfigRegistry(configRegistry);
this.natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
// Popup menu
this.natTable.addConfiguration(new HeaderMenuConfiguration(this.natTable) {
@Override
protected PopupMenuBuilder createColumnHeaderMenu(NatTable natTable) {
return super.createColumnHeaderMenu(natTable)
.withColumnChooserMenuItem();
}
});
this.natTable.addConfiguration(new SingleClickSortConfiguration());
// Editing
ColumnOverrideLabelAccumulator columnLabelAccumulator = new ColumnOverrideLabelAccumulator(
bodyLayer.getBodyDataLayer());
bodyLayer.getBodyDataLayer().setConfigLabelAccumulator(
columnLabelAccumulator);
this.natTable.addConfiguration(EditableGridExample
.editableGridConfiguration(columnLabelAccumulator,
this.bodyDataProvider));
this.natTable.addConfiguration(new FilterRowGridExample.FilterRowCustomConfiguration());
// Column chooser
DisplayColumnChooserCommandHandler columnChooserCommandHandler = new DisplayColumnChooserCommandHandler(
bodyLayer.getSelectionLayer(),
bodyLayer.getColumnHideShowLayer(),
columnHeaderLayer.getColumnHeaderLayer(),
columnHeaderLayer.getColumnHeaderDataLayer(),
columnHeaderLayer.getColumnGroupHeaderLayer(), columnGroupModel);
bodyLayer.registerCommandHandler(columnChooserCommandHandler);
// Summary row configuration
this.natTable.addConfiguration(new MySummaryRow<BlinkingRowDataFixture>(
this.bodyDataProvider));
this.natTable.configure();
return this.natTable;
}
private void setUpColumnGroups(
FullFeaturedColumnHeaderLayerStack<BlinkingRowDataFixture> headerLayer) {
headerLayer.getColumnGroupHeaderLayer().addColumnsIndexesToGroup(
"TestGroup", 1, 2);
headerLayer.getColumnGroupHeaderLayer().addColumnsIndexesToGroup(
"TestGroup1", 5, 6, 7);
headerLayer.getColumnGroupHeaderLayer().setGroupUnbreakable(5);
headerLayer.getColumnGroupHeaderLayer().setGroupAsCollapsed(5);
}
@Override
public void onStart() {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
_900_Everything_but_the_kitchen_sink.this.scheduledThreadPool = Executors.newScheduledThreadPool(1);
System.out.println("Starting data load.");
_900_Everything_but_the_kitchen_sink.this.scheduledThreadPool.schedule(new DataLoader(
_900_Everything_but_the_kitchen_sink.this.propertyChangeListener, _900_Everything_but_the_kitchen_sink.this.baseEventList), 100L,
TimeUnit.MILLISECONDS);
_900_Everything_but_the_kitchen_sink.this.scheduledThreadPool.scheduleAtFixedRate(new DataUpdater(
_900_Everything_but_the_kitchen_sink.this.bodyDataProvider), 100L, 5000L, TimeUnit.MILLISECONDS);
}
});
}
@Override
public void onStop() {
if (isNotNull(this.scheduledThreadPool)) {
this.scheduledThreadPool.shutdownNow();
}
}
private void registerBlinkingConfigCells(ConfigRegistry configRegistry) {
configRegistry.registerConfigAttribute(
BlinkConfigAttributes.BLINK_RESOLVER, getBlinkResolver(),
DisplayMode.NORMAL);
// Styles
Style cellStyle = new Style();
cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,
GUIHelper.COLOR_GREEN);
configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE,
cellStyle, DisplayMode.NORMAL, BLINK_UP_CONFIG_LABEL);
cellStyle = new Style();
cellStyle.setAttributeValue(CellStyleAttributes.BACKGROUND_COLOR,
GUIHelper.COLOR_RED);
configRegistry.registerConfigAttribute(CellConfigAttributes.CELL_STYLE,
cellStyle, DisplayMode.NORMAL, BLINK_DOWN_CONFIG_LABEL);
}
private IBlinkingCellResolver getBlinkResolver() {
return new BlinkingCellResolver() {
private final String[] configLabels = new String[1];
@Override
public String[] resolve(Object oldValue, Object newValue) {
double old = ((Double) oldValue).doubleValue();
double latest = ((Double) newValue).doubleValue();
this.configLabels[0] = (latest > old ? BLINK_UP_CONFIG_LABEL
: BLINK_DOWN_CONFIG_LABEL);
return this.configLabels;
}
};
}
private final Random random = new Random();
private static final int DATASET_SIZE = 52000;
/**
* Initial data load. Loads data in batches.
*
* Note: It is a significantly more efficient to do the load in batches i.e
* using {@link List#addAll(java.util.Collection)} instead of adding objects
* individually.
*
* @See https://sourceforge.net/projects/nattable/forums/forum/744994/topic/
* 3410065
*/
class DataLoader implements Runnable {
private final PropertyChangeListener changeListener;
private final EventList<BlinkingRowDataFixture> list;
private final String waitMsg = "Loading data. Please wait... ";
private WaitDialog dialog;
public DataLoader(PropertyChangeListener changeListener,
EventList<BlinkingRowDataFixture> baseEventList) {
this.changeListener = changeListener;
this.list = baseEventList;
}
@Override
public void run() {
try {
// Display wait dialog
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
final Image icon = new Image(Display.getDefault(),
getClass().getResourceAsStream("waiting.gif"));
DataLoader.this.dialog = new WaitDialog(_900_Everything_but_the_kitchen_sink.this.natTable.getShell(), SWT.NONE,
DataLoader.this.waitMsg, icon);
DataLoader.this.dialog.setBlockOnOpen(true);
DataLoader.this.dialog.open();
}
});
// Load data
while (this.list.size() < DATASET_SIZE) {
// Add to buffer
List<BlinkingRowDataFixture> buffer = new ArrayList<BlinkingRowDataFixture>();
for (int i = 0; i < 100; i++) {
buffer.addAll(BlinkingRowDataFixture
.getList(this.changeListener));
}
// Load as a batch
this.list.addAll(buffer);
// Update wait dialog
Display.getDefault().syncExec(new Runnable() {
@Override
public void run() {
DataLoader.this.dialog.setMsg(DataLoader.this.waitMsg + DataLoader.this.list.size());
}
});
}
// Close wait dialog
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
DataLoader.this.dialog.close();
}
});
} catch (Exception e) {
e.printStackTrace(System.err);
}
}
}
public Runnable runWithBusyIndicator(final Runnable runnable) {
return new Runnable() {
@Override
public void run() {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
BusyIndicator.showWhile(Display.getDefault(), runnable);
}
});
}
};
}
/**
* Pumps data updates to drive blinking
*/
class DataUpdater implements Runnable {
ListDataProvider<BlinkingRowDataFixture> dataProvider;
int counter = 0;
DataUpdater(ListDataProvider<BlinkingRowDataFixture> dataProvider) {
this.dataProvider = dataProvider;
}
@Override
public void run() {
Display.getDefault().asyncExec(new Runnable() {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
int index = _900_Everything_but_the_kitchen_sink.this.random.nextInt(13);
int nextAsk = _900_Everything_but_the_kitchen_sink.this.random.nextInt(1000);
if (DataUpdater.this.dataProvider.getRowCount() > index) {
BlinkingRowDataFixture rowObject = DataUpdater.this.dataProvider
.getRowObject(index);
// System.out.println("Ask:
// "+rowObject.getAsk_price()+" --> "+nextAsk);
rowObject.setAsk_price(nextAsk);
rowObject.setBid_price(-1 * nextAsk);
}
}
}
});
}
}
class MySummaryRow<T> extends DefaultSummaryRowConfiguration implements IConfiguration {
private IRowDataProvider<T> dataProvider;
public MySummaryRow(IRowDataProvider<T> dataProvider) {
this.dataProvider = dataProvider;
}
@Override
public void addSummaryProviderConfig(IConfigRegistry configRegistry) {
// Add summaries to ask price column
configRegistry.registerConfigAttribute(
SummaryRowConfigAttributes.SUMMARY_PROVIDER,
new SummationSummaryProvider(this.dataProvider),
DisplayMode.NORMAL,
SummaryRowLayer.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX
+ RowDataListFixture.getColumnIndexOfProperty(RowDataListFixture.ASK_PRICE_PROP_NAME));
// No Summary by default
configRegistry.registerConfigAttribute(
SummaryRowConfigAttributes.SUMMARY_PROVIDER,
ISummaryProvider.NONE,
DisplayMode.NORMAL,
SummaryRowLayer.DEFAULT_SUMMARY_ROW_CONFIG_LABEL);
}
}
}