| /******************************************************************************* |
| * 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); |
| } |
| } |
| |
| } |