| /******************************************************************************* |
| * Copyright (c) 2012, 2020 Original authors 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: |
| * Original authors and others - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.examples.examples._103_Events; |
| |
| import static java.util.concurrent.TimeUnit.MILLISECONDS; |
| import static org.eclipse.nebula.widgets.nattable.util.ObjectUtils.getRandomNumber; |
| |
| import java.io.Serializable; |
| import java.util.concurrent.Executors; |
| import java.util.concurrent.ScheduledExecutorService; |
| |
| import org.eclipse.nebula.widgets.nattable.NatTable; |
| import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration; |
| 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.RowDataFixture; |
| 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.fixtures.GlazedListsGridLayer; |
| import org.eclipse.nebula.widgets.nattable.examples.runner.StandaloneNatExampleRunner; |
| import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer; |
| import org.eclipse.nebula.widgets.nattable.layer.DataLayer; |
| import org.eclipse.nebula.widgets.nattable.selection.RowSelectionModel; |
| import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer; |
| import org.eclipse.nebula.widgets.nattable.selection.config.RowOnlySelectionBindings; |
| import org.eclipse.nebula.widgets.nattable.selection.config.RowOnlySelectionConfiguration; |
| import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.layout.GridData; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Button; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Text; |
| |
| import ca.odell.glazedlists.EventList; |
| import ca.odell.glazedlists.GlazedLists; |
| |
| /** |
| * Example to demonstrate rows being added/deleted as the backing data source is |
| * updated. |
| */ |
| public class Real_time_data_updates extends AbstractNatExample { |
| |
| private int updatesPerSecond = 1; |
| private final int defaultDatasetSize = 20; |
| |
| private ScheduledExecutorService scheduledThreadPool; |
| private NatTable nattable; |
| private EventList<RowDataFixture> eventList; |
| |
| public static void main(String[] args) { |
| StandaloneNatExampleRunner.run(500, 700, new Real_time_data_updates()); |
| } |
| |
| @Override |
| public String getDescription() { |
| return "Grid demonstrates data being added/removed. You can experiment with different data set sizes and update speeds.\n" |
| + "Select row by clicking on row header. Row selection is preserved when data is updated and sorted."; |
| } |
| |
| /** |
| * @see GlazedListsGridLayer to see the required stack setup. Basically the |
| * {@link DataLayer} needs to be wrapped up with a |
| * {@link GlazedListsEventLayer} and the backing list needs to be an |
| * {@link EventList} |
| */ |
| @Override |
| public Control createExampleControl(Composite parent) { |
| this.eventList = GlazedLists.eventList(RowDataListFixture |
| .getList(this.defaultDatasetSize)); |
| |
| ConfigRegistry configRegistry = new ConfigRegistry(); |
| GlazedListsGridLayer<RowDataFixture> glazedListsGridLayer = new GlazedListsGridLayer<>( |
| this.eventList, RowDataListFixture.getPropertyNames(), |
| RowDataListFixture.getPropertyToLabelMap(), configRegistry); |
| |
| this.nattable = new NatTable(parent, glazedListsGridLayer, false); |
| |
| this.nattable.setConfigRegistry(configRegistry); |
| this.nattable.addConfiguration(new DefaultNatTableStyleConfiguration()); |
| this.nattable.addConfiguration(new SingleClickSortConfiguration()); |
| |
| SelectionLayer selectionLayer = glazedListsGridLayer |
| .getBodyLayerStack() |
| .getSelectionLayer(); |
| ListDataProvider<RowDataFixture> bodyDataProvider = glazedListsGridLayer |
| .getBodyDataProvider(); |
| |
| // Select complete rows |
| RowOnlySelectionConfiguration selectionConfig = new RowOnlySelectionConfiguration(); |
| selectionLayer.addConfiguration(selectionConfig); |
| this.nattable.addConfiguration(new RowOnlySelectionBindings()); |
| |
| // Preserve selection on updates and sort |
| selectionLayer.setSelectionModel(new RowSelectionModel<>( |
| selectionLayer, bodyDataProvider, |
| new IRowIdAccessor<RowDataFixture>() { |
| |
| @Override |
| public Serializable getRowId(RowDataFixture rowObject) { |
| return rowObject.getSecurity_id(); |
| } |
| |
| })); |
| this.nattable.configure(); |
| |
| // Layout widgets |
| parent.setLayout(new GridLayout(1, true)); |
| this.nattable.setLayoutData(new GridData(GridData.FILL, GridData.FILL, true, |
| true)); |
| |
| setupTextArea(parent); |
| setupButtons(parent); |
| |
| return this.nattable; |
| } |
| |
| @Override |
| public void onStart() { |
| this.scheduledThreadPool = Executors.newScheduledThreadPool(1); |
| this.scheduledThreadPool.scheduleAtFixedRate(new ListEventsPublisher(), |
| 100L, 1000L / this.updatesPerSecond, MILLISECONDS); |
| } |
| |
| @Override |
| public void onStop() { |
| this.scheduledThreadPool.shutdown(); |
| this.nattable.dispose(); |
| } |
| |
| /** |
| * Utility class to make periodic modifications to the EventList. Each time |
| * its run, it randomly either removes an element or adds one. |
| */ |
| class ListEventsPublisher implements Runnable { |
| @Override |
| public void run() { |
| |
| Display.getDefault().asyncExec(new Runnable() { |
| @Override |
| public void run() { |
| try { |
| Real_time_data_updates.this.eventList.getReadWriteLock().writeLock().lock(); |
| |
| // Delete a random entry |
| int elementIndexToRemove = getRandomNumber(Real_time_data_updates.this.eventList |
| .size() - 1); |
| elementIndexToRemove = elementIndexToRemove < 0 ? 0 |
| : elementIndexToRemove; |
| |
| // Random decision |
| boolean removeElements = (getRandomNumber(1000) % 2) == 0; |
| |
| if (removeElements) { |
| final RowDataFixture remove = Real_time_data_updates.this.eventList |
| .remove(elementIndexToRemove); |
| log("Removed record: " + remove.getSecurity_id()); |
| } else { |
| final RowDataFixture added = RowDataFixture |
| .getInstance("Added by test", ("AAA")); |
| Real_time_data_updates.this.eventList.add(added); |
| log("Added record: " + added.getSecurity_id()); |
| } |
| } catch (Exception e) { |
| log("Ignoring exception: " + e.getMessage()); |
| } finally { |
| Real_time_data_updates.this.eventList.getReadWriteLock().writeLock().unlock(); |
| } |
| } |
| }); |
| } |
| } |
| |
| /** |
| * Adds the 'clear' button at the bottom |
| */ |
| private void setupButtons(Composite parent) { |
| Composite buttonComposite = new Composite(parent, SWT.NONE); |
| buttonComposite.setLayout(new GridLayout(3, false)); |
| buttonComposite.setLayoutData(new GridData(GridData.FILL, |
| GridData.FILL, true, false)); |
| |
| new Label(buttonComposite, SWT.NONE).setText("Clear and add records"); |
| |
| final Text clearText = new Text(buttonComposite, SWT.BORDER); |
| clearText.setText(Integer.toString(this.defaultDatasetSize)); |
| clearText.setLayoutData(new GridData(100, 14)); |
| clearText.setSize(100, 20); |
| |
| Button clearButton = new Button(buttonComposite, SWT.PUSH); |
| clearButton.setText("Apply"); |
| clearButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| final Integer newRecordCount = Integer.valueOf(clearText |
| .getText()); |
| try { |
| Real_time_data_updates.this.eventList.getReadWriteLock().writeLock().lock(); |
| Real_time_data_updates.this.eventList.clear(); |
| Real_time_data_updates.this.eventList.addAll(RowDataListFixture.getList(newRecordCount)); |
| } finally { |
| Real_time_data_updates.this.eventList.getReadWriteLock().writeLock().unlock(); |
| log(">> List cleared. Added " + newRecordCount |
| + " new records."); |
| } |
| } |
| }); |
| |
| new Label(buttonComposite, SWT.PUSH).setText("Updates/sec"); |
| |
| final Text updateSpeedText = new Text(buttonComposite, SWT.BORDER); |
| updateSpeedText.setLayoutData(new GridData(100, 14)); |
| updateSpeedText.setText(Integer.toString(this.updatesPerSecond)); |
| |
| Button updateSpeedButton = new Button(buttonComposite, SWT.PUSH); |
| updateSpeedButton.setText("Apply"); |
| updateSpeedButton.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| Real_time_data_updates.this.updatesPerSecond = Integer.parseInt(updateSpeedText.getText()); |
| log(">> Update speed: " + Real_time_data_updates.this.updatesPerSecond); |
| Real_time_data_updates.this.scheduledThreadPool.shutdownNow(); |
| onStart(); |
| } |
| }); |
| |
| final Button pauseButton = new Button(buttonComposite, SWT.PUSH); |
| pauseButton.setLayoutData(new GridData(100, 20)); |
| pauseButton.setText("Pause updates"); |
| pauseButton.addSelectionListener(new SelectionAdapter() { |
| boolean updatesPaused = false; |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| if (this.updatesPaused) { |
| log(">> Starting updates"); |
| this.updatesPaused = false; |
| pauseButton.setText("Pause updates"); |
| onStart(); |
| } else { |
| log(">> Pausing updates"); |
| this.updatesPaused = true; |
| pauseButton.setText("Start updates"); |
| Real_time_data_updates.this.scheduledThreadPool.shutdownNow(); |
| } |
| } |
| }); |
| |
| } |
| } |