blob: 644e99aeb0270dec1b8a9fb189695ab368a78ff2 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2010, 2020 Stephan Wahlbrink 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, or the Apache License, Version 2.0
# which is available at https://www.apache.org/licenses/LICENSE-2.0.
#
# SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
#
# Contributors:
# Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.r.ui.dataeditor;
import static org.eclipse.statet.ecommons.waltable.coordinate.Orientation.HORIZONTAL;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.concurrent.atomic.AtomicReference;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.operation.IRunnableContext;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.StackLayout;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.ui.progress.IProgressService;
import org.eclipse.ui.statushandlers.StatusManager;
import org.eclipse.statet.jcommons.status.ProgressMonitor;
import org.eclipse.statet.jcommons.status.Status;
import org.eclipse.statet.jcommons.status.StatusException;
import org.eclipse.statet.jcommons.ts.core.SystemRunnable;
import org.eclipse.statet.jcommons.ts.core.Tool;
import org.eclipse.statet.jcommons.ts.core.ToolRunnable;
import org.eclipse.statet.jcommons.ts.core.ToolService;
import org.eclipse.statet.ecommons.collections.FastList;
import org.eclipse.statet.ecommons.ui.util.LayoutUtils;
import org.eclipse.statet.ecommons.ui.util.UIAccess;
import org.eclipse.statet.ecommons.waltable.NatTable;
import org.eclipse.statet.ecommons.waltable.command.ILayerCommandHandler;
import org.eclipse.statet.ecommons.waltable.config.CellConfigAttributes;
import org.eclipse.statet.ecommons.waltable.config.IConfigRegistry;
import org.eclipse.statet.ecommons.waltable.config.LayoutSizeConfig;
import org.eclipse.statet.ecommons.waltable.coordinate.LRange;
import org.eclipse.statet.ecommons.waltable.coordinate.LRangeList;
import org.eclipse.statet.ecommons.waltable.coordinate.Orientation;
import org.eclipse.statet.ecommons.waltable.coordinate.PositionCoordinate;
import org.eclipse.statet.ecommons.waltable.coordinate.PositionId;
import org.eclipse.statet.ecommons.waltable.copy.CopyToClipboardCommandHandler;
import org.eclipse.statet.ecommons.waltable.data.ControlData;
import org.eclipse.statet.ecommons.waltable.data.IDataProvider;
import org.eclipse.statet.ecommons.waltable.data.ISpanningDataProvider;
import org.eclipse.statet.ecommons.waltable.freeze.CompositeFreezeLayer;
import org.eclipse.statet.ecommons.waltable.freeze.FreezeLayer;
import org.eclipse.statet.ecommons.waltable.grid.GridRegion;
import org.eclipse.statet.ecommons.waltable.grid.cell.AlternatingRowConfigLabelAccumulator;
import org.eclipse.statet.ecommons.waltable.grid.data.DefaultCornerDataProvider;
import org.eclipse.statet.ecommons.waltable.grid.labeled.ExtColumnHeaderLayer;
import org.eclipse.statet.ecommons.waltable.grid.labeled.ExtGridLayer;
import org.eclipse.statet.ecommons.waltable.grid.labeled.ExtRowHeaderLayer;
import org.eclipse.statet.ecommons.waltable.grid.labeled.LabelCornerLayer;
import org.eclipse.statet.ecommons.waltable.grid.layer.ColumnHeaderLayer;
import org.eclipse.statet.ecommons.waltable.grid.layer.CornerLayer;
import org.eclipse.statet.ecommons.waltable.grid.layer.GridLayer;
import org.eclipse.statet.ecommons.waltable.grid.layer.RowHeaderLayer;
import org.eclipse.statet.ecommons.waltable.layer.DataLayer;
import org.eclipse.statet.ecommons.waltable.layer.ILayerListener;
import org.eclipse.statet.ecommons.waltable.layer.SpanningDataLayer;
import org.eclipse.statet.ecommons.waltable.layer.cell.AggregrateConfigLabelAccumulator;
import org.eclipse.statet.ecommons.waltable.layer.event.ILayerEvent;
import org.eclipse.statet.ecommons.waltable.layer.event.IVisualChangeEvent;
import org.eclipse.statet.ecommons.waltable.layer.event.RowStructuralRefreshEvent;
import org.eclipse.statet.ecommons.waltable.layer.event.RowUpdateEvent;
import org.eclipse.statet.ecommons.waltable.resize.InitializeAutoResizeCommandHandler;
import org.eclipse.statet.ecommons.waltable.selection.ISelectionEvent;
import org.eclipse.statet.ecommons.waltable.selection.SelectAllCommand;
import org.eclipse.statet.ecommons.waltable.selection.SelectDimPositionsCommand;
import org.eclipse.statet.ecommons.waltable.selection.SelectRelativeCommandHandler;
import org.eclipse.statet.ecommons.waltable.selection.SelectionLayer;
import org.eclipse.statet.ecommons.waltable.sort.ClearSortCommand;
import org.eclipse.statet.ecommons.waltable.sort.ClearSortCommandHandler;
import org.eclipse.statet.ecommons.waltable.sort.ISortModel;
import org.eclipse.statet.ecommons.waltable.sort.SortDimPositionCommand;
import org.eclipse.statet.ecommons.waltable.sort.SortDirection;
import org.eclipse.statet.ecommons.waltable.sort.SortHeaderLayer;
import org.eclipse.statet.ecommons.waltable.sort.SortPositionCommandHandler;
import org.eclipse.statet.ecommons.waltable.style.DisplayMode;
import org.eclipse.statet.ecommons.waltable.tickupdate.config.DefaultTickUpdateConfiguration;
import org.eclipse.statet.ecommons.waltable.ui.ITableUIContext;
import org.eclipse.statet.ecommons.waltable.viewport.IViewportDim;
import org.eclipse.statet.ecommons.waltable.viewport.ViewportLayer;
import org.eclipse.statet.internal.r.ui.dataeditor.AbstractRDataProvider;
import org.eclipse.statet.internal.r.ui.dataeditor.FTableDataProvider;
import org.eclipse.statet.internal.r.ui.dataeditor.FindTask;
import org.eclipse.statet.internal.r.ui.dataeditor.IFindFilter;
import org.eclipse.statet.internal.r.ui.dataeditor.IFindListener;
import org.eclipse.statet.internal.r.ui.dataeditor.RDataFormatter;
import org.eclipse.statet.internal.r.ui.dataeditor.RDataFormatterConverter;
import org.eclipse.statet.internal.r.ui.dataeditor.RDataFrameDataProvider;
import org.eclipse.statet.internal.r.ui.dataeditor.RDataTableContentDescription;
import org.eclipse.statet.internal.r.ui.dataeditor.RMatrixDataProvider;
import org.eclipse.statet.internal.r.ui.dataeditor.RVectorDataProvider;
import org.eclipse.statet.internal.r.ui.dataeditor.ResolveCellIndexes;
import org.eclipse.statet.internal.r.ui.intable.PresentationConfig;
import org.eclipse.statet.internal.r.ui.intable.RDataLayer;
import org.eclipse.statet.internal.r.ui.intable.TableLayers;
import org.eclipse.statet.internal.r.ui.intable.UIBindings;
import org.eclipse.statet.r.ui.RUI;
import org.eclipse.statet.rj.data.RArray;
import org.eclipse.statet.rj.data.RCharacterStore;
import org.eclipse.statet.rj.data.RDataFrame;
import org.eclipse.statet.rj.data.RDataUtils;
import org.eclipse.statet.rj.data.RObject;
import org.eclipse.statet.rj.data.RObjectFactory;
import org.eclipse.statet.rj.data.RVector;
import org.eclipse.statet.rj.data.UnexpectedRDataException;
import org.eclipse.statet.rj.services.FunctionCall;
import org.eclipse.statet.rj.ts.core.RToolService;
public class RDataTableComposite extends Composite implements ISelectionProvider {
private class FindListener implements IFindListener {
@Override
public void handleFindEvent(final FindEvent event) {
if (RDataTableComposite.this.tableLayers != null) {
if (event.rowIdx >= 0) {
RDataTableComposite.this.tableLayers.setAnchor(event.colIdx, event.rowIdx, true);
}
for (final IFindListener listener : RDataTableComposite.this.findListeners.toArray()) {
listener.handleFindEvent(event);
}
}
}
}
private class SelectionFindFilter implements IFindFilter {
@Override
public boolean match(final long rowIdx, final long columnIdx) {
if (RDataTableComposite.this.tableLayers != null) {
if (columnIdx >= 0) {
return RDataTableComposite.this.tableLayers.selectionLayer.isCellPositionSelected(columnIdx, rowIdx);
}
else {
return RDataTableComposite.this.tableLayers.selectionLayer.isRowPositionSelected(rowIdx);
}
}
return false;
}
}
private class SetAnchorByDataIndexes extends ResolveCellIndexes {
public SetAnchorByDataIndexes(final AbstractRDataProvider<?> dataProvider) {
super(dataProvider);
}
@Override
protected void execute(final long columnIndex, final long rowIndex) {
RDataTableComposite.this.display.asyncExec(new Runnable() {
@Override
public void run() {
if (getDataProvider() != RDataTableComposite.this.dataProvider) {
return;
}
setAnchorViewIdxs(columnIndex, rowIndex);
}
});
}
}
private class RUIContext implements ITableUIContext {
public RUIContext() {
}
@Override
public void run(final boolean fork, final boolean cancelable,
final IRunnableWithProgress runnable)
throws InvocationTargetException, InterruptedException {
final IRunnableContext runnableContext= RDataTableComposite.this
.callbacks.getServiceLocator().getService(IProgressService.class);
runnableContext.run(fork, cancelable, new IRunnableWithProgress() {
@Override
public void run(final IProgressMonitor monitor)
throws InvocationTargetException, InterruptedException {
RDataTableComposite.this.dataProvider.beginOperation(this);
try {
runnable.run(monitor);
}
finally {
RDataTableComposite.this.dataProvider.endOperation(this);
}
}
});
}
@Override
public void show(final IStatus status) {
StatusManager.getManager().handle(status, StatusManager.SHOW);
}
}
private final Display display;
private final StackLayout layout;
private final Label messageControl;
private Composite reloadControl;
private final IRDataTableCallbacks callbacks;
private IRDataTableInput input;
private AbstractRDataProvider<?> dataProvider;
private TableLayers tableLayers;
private boolean tableInitialized;
private PositionCoordinate currentAnchor;
private PositionCoordinate currentLastSelectedCell;
private RDataTableSelection selection;
private final Object selectionLock= new Object();
private boolean selectionUpdateScheduled;
private long selectionUpdateScheduleStamp;
private boolean selectionCheckLabel;
private final Runnable selectionUpdateRunnable= new Runnable() {
@Override
public void run() {
if (isDisposed()) {
return;
}
updateSelection();
}
};
private final FastList<ISelectionChangedListener> selectionListeners= new FastList<>(ISelectionChangedListener.class);
private final FastList<IFindListener> findListeners= new FastList<>(IFindListener.class);
private final FastList<IRDataTableListener> tableListeners= new FastList<>(IRDataTableListener.class);
private final RDataFormatter formatter= new RDataFormatter();
private ResolveCellIndexes setAnchorByData;
private final FindListener findListener= new FindListener();
/**
* @param parent a widget which will be the parent of the new instance (cannot be null)
*/
public RDataTableComposite(final Composite parent, final IRDataTableCallbacks callbacks) {
super(parent, SWT.NONE);
if (callbacks == null) {
throw new NullPointerException("callbacks"); //$NON-NLS-1$
}
this.display= getDisplay();
this.callbacks= callbacks;
this.layout= new StackLayout();
setLayout(this.layout);
this.messageControl= new Label(this, SWT.NONE);
showDummy("Preparing...");
}
protected void initTable(final IRDataTableInput input,
final AbstractRDataProvider<? extends RObject> dataProvider) {
this.input= input;
this.dataProvider= dataProvider;
final PresentationConfig presentation= PresentationConfig.getInstance(this.display);
final TableLayers layers= new TableLayers();
layers.dataLayer= new RDataLayer(dataProvider, presentation.getBaseSizeConfig());
if (!this.dataProvider.getAllColumnsEqual()) {
// final ColumnOverrideLabelAccumulator columnLabelAccumulator =
// new ColumnOverrideLabelAccumulator(dataLayer);
// for (long i= 0; i < fDataProvider.getColumnCount(); i++) {
// columnLabelAccumulator.registerColumnOverrides(i, ColumnLabelAccumulator.COLUMN_LABEL_PREFIX + i);
// }
final AggregrateConfigLabelAccumulator aggregateLabelAccumulator =
new AggregrateConfigLabelAccumulator();
// aggregateLabelAccumulator.add(columnLabelAccumulator);
// dataLayer.setConfigLabelAccumulator(aggregateLabelAccumulator);
}
// final WColumnReorderLayer columnReorderLayer= new WColumnReorderLayer(dataLayer);
// final ColumnHideShowLayer columnHideShowLayer= new ColumnHideShowLayer(columnReorderLayer);
layers.selectionLayer= new SelectionLayer(layers.dataLayer, false);
layers.selectionLayer.addConfiguration(new UIBindings.SelectionConfiguration());
layers.selectionLayer.addConfiguration(new DefaultTickUpdateConfiguration());
layers.viewportLayer= new ViewportLayer(layers.selectionLayer);
{ final FreezeLayer freezeLayer= new FreezeLayer(layers.selectionLayer);
final CompositeFreezeLayer compositeFreezeLayer= new CompositeFreezeLayer(freezeLayer,
layers.viewportLayer, layers.selectionLayer, true);
layers.topBodyLayer= compositeFreezeLayer;
}
{ final IDataProvider headerDataProvider= dataProvider.getColumnDataProvider();
layers.dataColumnHeaderLayer= (headerDataProvider instanceof ISpanningDataProvider) ?
new SpanningDataLayer((ISpanningDataProvider) headerDataProvider,
PositionId.BODY_CAT, 10,
PositionId.HEADER_CAT, 10 ) :
new DataLayer(headerDataProvider,
PositionId.BODY_CAT, 10,
PositionId.HEADER_CAT, 10 );
final ColumnHeaderLayer layer= new ColumnHeaderLayer(
layers.dataColumnHeaderLayer,
layers.topBodyLayer, layers.selectionLayer,
false, presentation.getHeaderLayerPainter() );
layer.addConfiguration(new UIBindings.ColumnHeaderConfiguration());
layers.topColumnHeaderLayer= layer;
}
final ISortModel sortModel= dataProvider.getSortModel();
if (sortModel != null) {
final SortHeaderLayer<?> sortHeaderLayer= new SortHeaderLayer<>(
layers.topColumnHeaderLayer, sortModel, false);
sortHeaderLayer.addConfiguration(new UIBindings.SortConfiguration());
layers.topColumnHeaderLayer= sortHeaderLayer;
}
{ final IDataProvider headerDataProvider= dataProvider.getRowDataProvider();
layers.dataRowHeaderLayer= (headerDataProvider instanceof ISpanningDataProvider) ?
new SpanningDataLayer((ISpanningDataProvider) headerDataProvider,
PositionId.HEADER_CAT, 10,
PositionId.BODY_CAT, 10 ) :
new DataLayer(headerDataProvider,
PositionId.HEADER_CAT, 10,
PositionId.BODY_CAT, 10 );
layers.topRowHeaderLayer= new RowHeaderLayer(
layers.dataRowHeaderLayer,
layers.topBodyLayer, layers.selectionLayer,
false, presentation.getHeaderLayerPainter() );
}
final IDataProvider cornerDataProvider= new DefaultCornerDataProvider(
layers.dataColumnHeaderLayer.getDataProvider(),
layers.dataRowHeaderLayer.getDataProvider() );
final GridLayer gridLayer;
if (dataProvider.getColumnLabelProvider() != null || dataProvider.getRowLabelProvider() != null) {
layers.topColumnHeaderLayer= new ExtColumnHeaderLayer(layers.topColumnHeaderLayer);
layers.topRowHeaderLayer= new ExtRowHeaderLayer(layers.topRowHeaderLayer);
final CornerLayer cornerLayer= new LabelCornerLayer(
new DataLayer(cornerDataProvider,
PositionId.HEADER_CAT, 10,
PositionId.HEADER_CAT, 10 ),
layers.topRowHeaderLayer, layers.topColumnHeaderLayer,
dataProvider.getColumnLabelProvider(), dataProvider.getRowLabelProvider(),
false, presentation.getHeaderLabelLayerPainter() );
gridLayer= new ExtGridLayer(layers.topBodyLayer,
layers.topColumnHeaderLayer, layers.topRowHeaderLayer,
cornerLayer, false );
}
else {
final CornerLayer cornerLayer= new CornerLayer(
new DataLayer(cornerDataProvider, PositionId.HEADER_CAT),
layers.topRowHeaderLayer, layers.topColumnHeaderLayer,
false, presentation.getHeaderLayerPainter() );
gridLayer= new GridLayer(layers.topBodyLayer,
layers.topColumnHeaderLayer, layers.topRowHeaderLayer, cornerLayer, false);
}
gridLayer.addConfigLabelAccumulatorForRegion(GridRegion.BODY, new AlternatingRowConfigLabelAccumulator());
final Runnable configRunnable= new Runnable() {
@Override
public void run() {
final TableLayers layers= RDataTableComposite.this.tableLayers;
if (layers == null) {
return;
}
final LayoutSizeConfig sizeConfig= presentation.getBaseSizeConfig();
layers.dataLayer.setSizeConfig(sizeConfig);
layers.dataColumnHeaderLayer.setDefaultRowHeight(sizeConfig.getRowHeight());
layers.dataRowHeaderLayer.setDefaultColumnWidth(sizeConfig.getCharWidth() * 8 + sizeConfig.getDefaultSpace() * 2);
if (layers.topColumnHeaderLayer instanceof ExtColumnHeaderLayer) {
((ExtColumnHeaderLayer) layers.topColumnHeaderLayer).setSpaceSize(sizeConfig.getRowHeight());
((ExtRowHeaderLayer) layers.topRowHeaderLayer).setSpaceSize(sizeConfig.getRowHeight());
}
presentation.configureRegistry(layers.table.getConfigRegistry());
layers.table.updateResize();
}
};
presentation.addListener(configRunnable);
final ITableUIContext uiContext= new RUIContext();
// { final ILayerCommandHandler<?> commandHandler= new ScrollCommandHandler(fTableLayers.viewportLayer);
// fTableLayers.viewportLayer.registerCommandHandler(commandHandler);
// }
{ final ILayerCommandHandler<?> commandHandler= new SelectRelativeCommandHandler(
layers.selectionLayer );
layers.viewportLayer.registerCommandHandler(commandHandler);
layers.selectionLayer.registerCommandHandler(commandHandler);
}
{ final ILayerCommandHandler<?> commandHandler= new CopyToClipboardCommandHandler(
layers.selectionLayer, uiContext );
layers.selectionLayer.registerCommandHandler(commandHandler);
}
{ final ILayerCommandHandler<?> commandHandler= new InitializeAutoResizeCommandHandler(
layers.selectionLayer );
gridLayer.registerCommandHandler(commandHandler);
}
if (sortModel != null) {
final ILayerCommandHandler<?> commandHandler= new SortPositionCommandHandler(sortModel);
layers.dataLayer.registerCommandHandler(commandHandler);
}
if (sortModel != null) {
final ILayerCommandHandler<?> commandHandler= new ClearSortCommandHandler(sortModel);
layers.dataLayer.registerCommandHandler(commandHandler);
}
layers.table= new NatTable(this, gridLayer, false);
layers.table.addConfiguration(presentation);
layers.table.addConfiguration(new UIBindings.HeaderContextMenuConfiguration(layers.table));
layers.table.addConfiguration(new UIBindings.BodyContextMenuConfiguration(layers.table,
this.callbacks.getServiceLocator() ));
layers.table.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(final DisposeEvent e) {
dataProvider.dispose();
}
});
final IConfigRegistry registry= layers.table.getConfigRegistry();
registry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
new RDataFormatterConverter(dataProvider));
registry.registerConfigAttribute(CellConfigAttributes.DISPLAY_CONVERTER,
new RDataFormatterConverter.RowHeader(dataProvider),
DisplayMode.NORMAL, GridRegion.ROW_HEADER);
this.tableLayers= layers;
this.tableInitialized= false;
configRunnable.run();
this.tableLayers.table.addLayerListener(new ILayerListener() {
@Override
public void handleLayerEvent(final ILayerEvent event) {
if (event instanceof ISelectionEvent) {
scheduleUpdateSelection(false, 100);
return;
}
if (event instanceof IVisualChangeEvent) {
scheduleUpdateSelection(true, 100);
return;
}
}
});
dataProvider.addDataChangedListener(new AbstractRDataProvider.IDataProviderListener() {
@Override
public void onInputInitialized(final boolean structChanged) {
if (layers != RDataTableComposite.this.tableLayers || layers.table.isDisposed()) {
return;
}
if (layers.table != RDataTableComposite.this.layout.topControl) {
layers.table.configure();
}
if (layers.table != RDataTableComposite.this.layout.topControl) {
RDataTableComposite.this.layout.topControl= layers.table;
layout();
}
if (!RDataTableComposite.this.tableInitialized) {
layers.selectionLayer.setSelectedCell(0, 0);
}
else if (structChanged) {
layers.dataLayer.fireLayerEvent(new RowStructuralRefreshEvent(layers.dataLayer));
}
else {
layers.dataLayer.fireLayerEvent(new RowUpdateEvent(layers.dataLayer,
new LRange(0, layers.dataLayer.getRowCount()) ));
}
RDataTableComposite.this.selection= null;
scheduleUpdateSelection(true, 0);
for (final IRDataTableListener listener : RDataTableComposite.this.tableListeners.toArray()) {
listener.inputChanged(input, dataProvider.getDescription());
}
}
@Override
public void onInputFailed(final int error) {
if (error == ERROR_STRUCT_CHANGED) {
showReload();
}
else {
showDummy("An error occurred when loading the table input.");
for (final IRDataTableListener listener : RDataTableComposite.this.tableListeners.toArray()) {
listener.inputChanged(null, null);
}
}
}
@Override
public void onRowCountChanged() {
final TableLayers layers= RDataTableComposite.this.tableLayers;
if (layers == null || layers.table != layers.table || layers.table.isDisposed()) {
return;
}
layers.dataLayer.fireLayerEvent(new RowStructuralRefreshEvent(layers.dataLayer));
RDataTableComposite.this.selection= null;
scheduleUpdateSelection(true, 0);
}
@Override
public void onRowsChanged(final long beginIdx, final long endIdx) {
RDataTableComposite.this.display.asyncExec(new Runnable() {
@Override
public void run() {
final TableLayers layers= RDataTableComposite.this.tableLayers;
if (layers == null || layers.table != layers.table || layers.table.isDisposed()) {
return;
}
layers.dataLayer.fireLayerEvent(new RowUpdateEvent(layers.dataLayer,
new LRange(beginIdx, endIdx) ));
}
});
}
});
dataProvider.addFindListener(this.findListener);
}
public NatTable getTable() {
return (this.tableLayers != null) ? this.tableLayers.table : null;
}
protected void scheduleUpdateSelection(final boolean checkLabel, final int delay) {
synchronized (this.selectionLock) {
this.selectionUpdateScheduleStamp= System.nanoTime() + delay * 1000000L;
if (checkLabel) {
this.selectionCheckLabel= true;
}
if (this.selectionUpdateScheduled) {
return;
}
this.selectionUpdateScheduled= true;
}
this.display.asyncExec(this.selectionUpdateRunnable);
}
protected void updateSelection() {
final boolean checkLabel;
synchronized (this.selectionLock) {
final long diff= (this.selectionUpdateScheduleStamp - System.nanoTime()) / 1000000L;
if (diff > 5) {
this.display.timerExec((int) diff, this.selectionUpdateRunnable);
return;
}
checkLabel= this.selectionCheckLabel;
this.selectionCheckLabel= false;
this.selectionUpdateScheduled= false;
}
final RDataTableSelection selection;
if (this.tableLayers == null) {
selection= new RDataTableSelection(null, null, null, null);
}
else {
final SelectionLayer selectionLayer= this.tableLayers.selectionLayer;
final PositionCoordinate anchor= selectionLayer.getSelectionAnchor();
PositionCoordinate lastSelected= selectionLayer.getLastSelectedCellPosition();
if (lastSelected.equals(anchor)) {
lastSelected= null;
}
final boolean anchorChanged;
if ((anchorChanged= !anchor.equals(this.currentAnchor))) {
this.currentAnchor= new PositionCoordinate(anchor);
}
final boolean lastSelectedChanged;
if ((lastSelectedChanged= !((lastSelected != null) ?
lastSelected.equals(this.currentLastSelectedCell) : null == this.currentLastSelectedCell ))) {
this.currentLastSelectedCell= (lastSelected != null) ? new PositionCoordinate(lastSelected) : null;
}
if (!checkLabel && !anchorChanged && !lastSelectedChanged) {
return;
}
String anchorRowLabel= null;
String anchorColumnLabel= null;
if (this.currentAnchor.columnPosition >= 0 && this.currentAnchor.rowPosition >= 0) {
if (anchorChanged || checkLabel) {
anchorRowLabel= getRowLabel(this.currentAnchor.rowPosition);
if (anchorRowLabel != null) {
anchorColumnLabel= getColumnLabel(this.currentAnchor.columnPosition);
if (anchorColumnLabel == null) {
anchorRowLabel= null;
}
}
}
else if (this.selection != null) {
anchorRowLabel= this.selection.getAnchorRowLabel();
anchorColumnLabel= this.selection.getAnchorColumnLabel();
}
}
if (anchorRowLabel == null) {
return;
}
String lastSelectedRowLabel= null;
String lastSelectedColumnLabel= null;
if (this.currentLastSelectedCell != null
&& this.currentLastSelectedCell.columnPosition >= 0 && this.currentLastSelectedCell.rowPosition >= 0) {
if (lastSelectedChanged || checkLabel) {
lastSelectedRowLabel= getRowLabel(this.currentLastSelectedCell.rowPosition);
if (lastSelectedRowLabel != null) {
lastSelectedColumnLabel= getColumnLabel(this.currentLastSelectedCell.columnPosition);
if (lastSelectedColumnLabel == null) {
lastSelectedRowLabel= null;
}
}
}
else if (this.selection != null) {
lastSelectedRowLabel= this.selection.getLastSelectedCellRowLabel();
lastSelectedColumnLabel= this.selection.getLastSelectedCellColumnLabel();
}
}
selection= new RDataTableSelection(
anchorRowLabel, anchorColumnLabel,
lastSelectedRowLabel, lastSelectedColumnLabel);
}
if (selection.equals(this.selection)) {
return;
}
this.selection= selection;
final SelectionChangedEvent event= new SelectionChangedEvent(this, this.selection);
for (final ISelectionChangedListener listener : this.selectionListeners.toArray()) {
listener.selectionChanged(event);
}
}
protected String getRowLabel(final long row) {
if (!this.dataProvider.hasRealRows()) {
return ""; //$NON-NLS-1$
}
final IDataProvider dataProvider= this.dataProvider.getRowDataProvider();
if (dataProvider.getColumnCount() <= 1) {
return getHeaderLabel(dataProvider.getDataValue(0, row, 0, null));
}
final StringBuilder sb= new StringBuilder();
for (long i= 0; i < dataProvider.getColumnCount(); i++) {
final String label= getHeaderLabel(dataProvider.getDataValue(i, row, 0, null));
if (label == null) {
return null;
}
sb.append(label);
sb.append(", "); //$NON-NLS-1$
}
return sb.substring(0, sb.length()-2);
}
protected String getColumnLabel(final long column) {
if (!this.dataProvider.hasRealColumns()) {
return ""; //$NON-NLS-1$
}
final IDataProvider dataProvider= this.dataProvider.getColumnDataProvider();
if (dataProvider.getRowCount() <= 1) {
return getHeaderLabel(dataProvider.getDataValue(column, 0, 0, null));
}
final StringBuilder sb= new StringBuilder();
for (long i= 0; i < dataProvider.getRowCount(); i++) {
final String label= getHeaderLabel(dataProvider.getDataValue(column, i, 0, null));
if (label == null) {
return null;
}
sb.append(label);
sb.append(", "); //$NON-NLS-1$
}
return sb.substring(0, sb.length()-2);
}
private String getHeaderLabel(final Object value) {
if (value != null) {
if (value instanceof ControlData && value != AbstractRDataProvider.NA) {
return null;
}
final Object displayValue= this.formatter.modelToDisplayValue(value);
if (displayValue.getClass() == String.class) {
return (String) displayValue;
}
if (displayValue == AbstractRDataProvider.DUMMY) {
return ""; //$NON-NLS-1$
}
return null;
}
else {
return this.formatter.modelToDisplayValue(null).toString();
}
}
protected void showDummy(final String message) {
if (isDisposed()) {
return;
}
this.messageControl.setText(message);
this.layout.topControl= this.messageControl;
layout();
}
public long[] getTableDimension() {
if (this.tableLayers != null) {
return new long[] { this.dataProvider.getRowCount(), this.dataProvider.getColumnCount() };
}
return null;
}
public boolean isOK() {
return (this.tableLayers != null && this.layout.topControl == this.tableLayers.table);
}
public IViewportDim getViewport(final Orientation orientation) {
return this.tableLayers.viewportLayer.getDim(orientation);
}
@Override
public ISelection getSelection() {
return null;
}
@Override
public void setSelection(final ISelection selection) {
}
@Override
public void addSelectionChangedListener(final ISelectionChangedListener listener) {
this.selectionListeners.add(listener);
}
@Override
public void removeSelectionChangedListener(final ISelectionChangedListener listener) {
this.selectionListeners.remove(listener);
}
public void find(final String expression, final boolean selectedOnly, final boolean firstInRow, final boolean forward) {
if (this.tableLayers != null) {
final PositionCoordinate anchor= this.tableLayers.selectionLayer.getSelectionAnchor();
final FindTask task= new FindTask(expression,
anchor.getRowPosition(), anchor.getColumnPosition(), firstInRow, forward,
(selectedOnly) ? new SelectionFindFilter() : null);
this.dataProvider.find(task);
}
}
public void addFindListener(final IFindListener listener) {
this.findListeners.add(listener);
}
public void removeFindListener(final IFindListener listener) {
this.findListeners.remove(listener);
}
public void setInput(final IRDataTableInput input) {
if (this.tableLayers != null) {
showDummy(""); //$NON-NLS-1$
this.tableLayers.table.dispose();
this.tableLayers.table= null;
this.dataProvider= null;
this.setAnchorByData= null;
}
if (input != null) {
showDummy("Preparing (" + input.getName() + ")...");
try {
final ToolRunnable runnable= new SystemRunnable() {
@Override
public String getTypeId() {
return "r/dataeditor/init"; //$NON-NLS-1$
}
@Override
public String getLabel() {
return "Prepare Data Viewer (" + input.getName() + ")";
}
@Override
public boolean canRunIn(final Tool tool) {
return true; // TODO
}
@Override
public boolean changed(final int event, final Tool process) {
if (event == MOVING_FROM) {
return false;
}
return true;
}
@Override
public void run(final ToolService service,
final ProgressMonitor m) throws StatusException {
final RToolService r= (RToolService) service;
final AtomicReference<AbstractRDataProvider<?>> dataProvider= new AtomicReference<>();
Exception error= null;
try {
final RObject struct= r.evalData(input.getFullName(),
null, RObjectFactory.F_ONLY_STRUCT, 1, m );
RCharacterStore classNames= null;
{ final FunctionCall call= r.createFunctionCall("class"); //$NON-NLS-1$
call.add(input.getFullName());
classNames= RDataUtils.checkRCharVector(call.evalData(m)).getData();
}
switch (struct.getRObjectType()) {
case RObject.TYPE_VECTOR:
dataProvider.set(new RVectorDataProvider(input, (RVector<?>) struct));
break;
case RObject.TYPE_ARRAY: {
final RArray<?> array= (RArray<?>) struct;
if (array.getDim().getLength() == 2) {
if (classNames.contains("ftable")) { //$NON-NLS-1$
dataProvider.set(new FTableDataProvider(input, array));
break;
}
dataProvider.set(new RMatrixDataProvider(input, array));
break;
}
break; }
case RObject.TYPE_DATAFRAME:
dataProvider.set(new RDataFrameDataProvider(input, (RDataFrame) struct));
break;
default:
break;
}
}
catch (final CoreException e) {
error= e;
}
catch (final UnexpectedRDataException e) {
error= e;
}
final IStatus status;
if (error != null) {
status= new org.eclipse.core.runtime.Status(IStatus.ERROR, RUI.BUNDLE_ID,
"An error occurred when preparing the R data viewer.", error );
StatusManager.getManager().handle(status);
}
else if (dataProvider.get() == null) {
status= new org.eclipse.core.runtime.Status(IStatus.ERROR, RUI.BUNDLE_ID,
"This R element type is not supported.", null );
}
else {
status= null;
}
RDataTableComposite.this.display.asyncExec(new Runnable() {
@Override
public void run() {
RDataTableComposite.this.dataProvider= null;
if (!UIAccess.isOkToUse(RDataTableComposite.this)) {
return;
}
if (status == null) {
initTable(input, dataProvider.get());
}
else {
showDummy(status.getMessage());
}
}
});
}
};
final Status status= input.getTool().getQueue().add(runnable);
if (status.getSeverity() >= Status.ERROR) {
throw new StatusException(status);
}
}
catch (final StatusException e) {
showDummy(e.getLocalizedMessage());
}
}
}
protected void showReload() {
if (this.reloadControl == null) {
this.reloadControl= new Composite(this, SWT.NONE);
this.reloadControl.setLayout(LayoutUtils.newCompositeGrid(4));
final Label label= new Label(this.reloadControl, SWT.WRAP);
label.setText("The structure of the R element is changed (columns / data type).");
label.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 4, 1));
{ final Button button= new Button(this.reloadControl, SWT.PUSH);
button.setLayoutData(LayoutUtils.hintWidth(new GridData(
SWT.FILL, SWT.CENTER, false, false), button));
button.setText("Refresh");
button.setToolTipText("Refresh table with old structure");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
refresh();
}
});
}
{ final Button button= new Button(this.reloadControl, SWT.PUSH);
button.setLayoutData(LayoutUtils.hintWidth(new GridData(
SWT.FILL, SWT.CENTER, false, false), button));
button.setText("Reopen");
button.setToolTipText("Reopen table with new structure");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
setInput(RDataTableComposite.this.input);
}
});
}
if (this.callbacks.isCloseSupported()) {
final Button button= new Button(this.reloadControl, SWT.PUSH);
button.setLayoutData(LayoutUtils.hintWidth(new GridData(
SWT.FILL, SWT.CENTER, false, false), button));
button.setText("Close");
button.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
RDataTableComposite.this.callbacks.close();
}
});
}
//
LayoutUtils.addSmallFiller(this.reloadControl, true);
}
this.layout.topControl= this.reloadControl;
layout(true);
}
public void setFilter(final String filter) {
this.dataProvider.setFilter(filter);
}
public RDataTableContentDescription getDescription() {
return this.dataProvider.getDescription();
}
public void addTableListener(final IRDataTableListener listener) {
this.tableListeners.add(listener);
if (this.tableLayers != null) {
listener.inputChanged(this.input, this.dataProvider.getDescription());
}
}
public void removeTableListener(final IRDataTableListener listener) {
this.tableListeners.remove(listener);
}
@Override
public boolean setFocus() {
if (this.layout == null || this.layout.topControl == null) {
return false;
}
return this.layout.topControl.forceFocus();
}
public void revealColumn(final long index) {
if (this.tableLayers != null) {
this.tableLayers.viewportLayer.getDim(HORIZONTAL).movePositionIntoViewport(index);
}
}
public void selectColumns(final Collection<LRange> indexes) {
if (this.tableLayers != null) {
final LRangeList columns= LRangeList.toRangeList(indexes);
// final long rowIndex= fTableBodyLayerStack.getViewportLayer().getRowIndexByPosition(0);
final long rowIndex= 0;
this.tableLayers.table.doCommand(new SelectDimPositionsCommand(
this.tableLayers.selectionLayer.getDim(HORIZONTAL),
0, columns, rowIndex,
0,
!(columns.isEmpty()) ? columns.values().first() : SelectionLayer.NO_SELECTION ));
}
}
public void setAnchorViewIdxs(final long columnIndex, final long rowIndex) {
if (this.tableLayers != null) {
this.tableLayers.selectionLayer.setSelectionAnchor(columnIndex, rowIndex, true);
}
}
public long[] getAnchorDataIdxs() {
if (this.tableLayers != null) {
final PositionCoordinate coordinate= this.tableLayers.selectionLayer.getSelectionAnchor();
if (coordinate.columnPosition < 0 || coordinate.rowPosition < 0) {
return null;
}
return this.dataProvider.toDataIdxs(coordinate.columnPosition, coordinate.rowPosition);
}
return null;
}
public void setAnchorDataIdxs(final long columnIdx, final long rowIdx) {
if (this.tableLayers != null) {
if (this.setAnchorByData == null) {
this.setAnchorByData= new SetAnchorByDataIndexes(this.dataProvider);
}
this.setAnchorByData.resolve(columnIdx, rowIdx);
}
}
public void selectAll() {
if (this.tableLayers != null) {
this.tableLayers.table.doCommand(new SelectAllCommand());
}
}
public void sortByColumn(final long index, final boolean increasing) {
if (this.tableLayers != null) {
this.tableLayers.dataLayer.doCommand(new SortDimPositionCommand(
this.tableLayers.dataLayer.getDim(HORIZONTAL), index,
increasing ? SortDirection.ASC : SortDirection.DESC, false ));
// final ISortModel sortModel= this.fDataProvider.getSortModel();
// if (sortModel != null) {
// sortModel.sort(
// PositionId.BODY_CAT | index, increasing ? SortDirection.ASC : SortDirection.DESC, false );
// this.fTableLayers.topColumnHeaderLayer.fireLayerEvent(
// new SortColumnEvent(this.fTableLayers.topColumnHeaderLayer.getDim(HORIZONTAL), 0) );
// }
}
}
public void clearSorting() {
if (this.tableLayers != null) {
this.tableLayers.table.doCommand(new ClearSortCommand());
}
}
public void refresh() {
if (this.tableLayers != null) {
this.dataProvider.reset();
this.tableLayers.table.redraw();
}
}
}