blob: 65e0328218e2f90e940966715c197708cbeb5c73 [file] [log] [blame]
/*=============================================================================#
# Copyright (c) 2013, 2021 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@gmail.com> - initial API and implementation
#=============================================================================*/
package org.eclipse.statet.ecommons.waltable.edit.gui;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.statet.ecommons.waltable.Messages;
import org.eclipse.statet.ecommons.waltable.config.IConfigRegistry;
import org.eclipse.statet.ecommons.waltable.data.validate.IDataValidator;
import org.eclipse.statet.ecommons.waltable.edit.EditConfigAttributes;
import org.eclipse.statet.ecommons.waltable.edit.EditMode;
import org.eclipse.statet.ecommons.waltable.edit.EditTypeEnum;
import org.eclipse.statet.ecommons.waltable.edit.editor.AbstractCellEditor;
import org.eclipse.statet.ecommons.waltable.edit.editor.ICellEditor;
import org.eclipse.statet.ecommons.waltable.layer.cell.ILayerCell;
import org.eclipse.statet.ecommons.waltable.style.DisplayMode;
import org.eclipse.statet.ecommons.waltable.tickupdate.ITickUpdateHandler;
import org.eclipse.statet.ecommons.waltable.tickupdate.TickUpdateConfigAttributes;
import org.eclipse.statet.internal.ecommons.waltable.WaLTablePlugin;
/**
* Specialization of {@link CellEditDialog} that adds additional functionality
* to also use tick updates on the cell value(s). By selecting another edit type
* than set, the value entered in the editor control will be processed with the
* current value instead of simply setting this value. As using e.g. increase/decrease
* on the current value allows different values than on set, the validator needs
* to be wrapped so it is skipped for entering the value for processing.
*/
public class TickUpdateCellEditDialog extends CellEditDialog {
private static final String SET= Messages.getString("TickUpdateCellEditDialog.set"); //$NON-NLS-1$
private static final String INCREASE_BY= Messages.getString("TickUpdateCellEditDialog.increase"); //$NON-NLS-1$
private static final String DECREASE_BY= Messages.getString("TickUpdateCellEditDialog.decrease"); //$NON-NLS-1$
private static final String ADJUST_BY = Messages.getString("TickUpdateCellEditDialog.adjust"); //$NON-NLS-1$
private static final String [] OPTIONS_DEFAULT= {SET, INCREASE_BY, DECREASE_BY};
private static final String [] OPTIONS_ADJUST= {SET, ADJUST_BY};
/**
* The {@link ITickUpdateHandler} that will be used to process
* the tick updates after closing this editor by pressing ok.
*/
private final ITickUpdateHandler tickUpdateHandler;
/**
* The combo control that contains the available actions used for tick update.
*/
private Combo updateCombo;
/**
* The {@link EditTypeEnum} that represents the selected item in the tick update
* combo control.
*/
private EditTypeEnum editType= EditTypeEnum.SET;
/**
* Flag to determine whether this dialog should provide the set, increase and decrease
* tick update options, or the set and adjust options. Default is to not use the adjust
* options.
*/
private final boolean useAdjustBy;
/**
* The validator that is needed to be called when the tick update is processed.
* On executing a tick update it would otherwise be possible to exploit validation.
*/
private IDataValidator validator;
/**
*
* @param parentShell the parent shell, or <code>null</code> to create a top-level
* shell
* @param originalCanonicalValue The value that should be propagated to the editor
* control. Needed because for multi cell editing or editor activation by
* letter/digit key will result in a different value to populate for some
* editors than populating the value out of the cell/data model directly.
* @param cell The cell that should be edited. Needed because editor activation
* retrieves the configuration for editing directly out of the cell.
* @param cellEditor The {@link ICellEditor} that will be used as editor control
* within the dialog.
* @param configRegistry The {@link IConfigRegistry} containing the configuration of
* the current NatTable instance the command should be executed for. This is
* necessary because the edit controllers in the current architecture are not
* aware of the instance they are running in and therefore it is needed for
* activation of editors.
* @param tickUpdateHandler The {@link ITickUpdateHandler} that will be used to process
* the tick updates after closing this editor by pressing ok.
*/
public TickUpdateCellEditDialog(final Shell parentShell,
final Object originalCanonicalValue,
final ILayerCell cell,
final ICellEditor cellEditor,
final IConfigRegistry configRegistry,
final ITickUpdateHandler tickUpdateHandler) {
super(parentShell, originalCanonicalValue, cell, cellEditor, configRegistry);
this.tickUpdateHandler= tickUpdateHandler;
final Boolean useAdjustByConfig= configRegistry.getConfigAttribute(
TickUpdateConfigAttributes.USE_ADJUST_BY,
DisplayMode.EDIT,
cell.getConfigLabels().getLabels());
this.useAdjustBy= useAdjustByConfig != null ? useAdjustByConfig : false;
}
@Override
protected Control createDialogArea(final Composite parent) {
final Composite panel= new Composite(parent, SWT.NONE);
GridDataFactory.fillDefaults().grab(true, true).applyTo(panel);
final GridLayout panelLayout= new GridLayout(2, false);
panelLayout.marginWidth= 8;
panel.setLayout(panelLayout);
createUpdateCombo(panel);
//activate the new editor
this.cellEditor.activateCell(
panel,
this.originalCanonicalValue,
EditMode.DIALOG,
this.cellEditHandler,
this.cell,
this.configRegistry);
this.validator= this.configRegistry.getConfigAttribute(
EditConfigAttributes.DATA_VALIDATOR,
DisplayMode.EDIT,
this.cell.getConfigLabels().getLabels());
if (this.cellEditor instanceof AbstractCellEditor) {
//need to override the validator because increase decrease adjust don't need to validate immediately
((AbstractCellEditor) this.cellEditor).setDataValidator(
new TickUpdateDataValidatorWrapper(this.validator));
}
final Control editorControl= this.cellEditor.getEditorControl();
// propagate the ESC event from the editor to the dialog
editorControl.addKeyListener(getEscKeyListener());
GridDataFactory.fillDefaults().grab(true, false).hint(100, 20).indent(5, 0)
.applyTo(editorControl);
//if the editor control already has no layout data set already, apply the default one
//this check allows to specify a custom layout data while creating the editor control
//in the ICellEditor
if (editorControl.getLayoutData() == null) {
GridDataFactory.fillDefaults().grab(true, false).hint(100, 20).applyTo(editorControl);
}
return panel;
}
/**
* Create the combo control that contains the available actions used for tick update.
* The possible actions in this combo are specified by evaluating the useAdjustBy configuration.
* @param composite The composite control that will be the parent for the tick update combo.
*/
private void createUpdateCombo(final Composite composite) {
this.updateCombo= new Combo(composite, SWT.READ_ONLY | SWT.DROP_DOWN | SWT.BORDER);
for (final String option : this.useAdjustBy ? OPTIONS_ADJUST : OPTIONS_DEFAULT) {
this.updateCombo.add(option);
}
this.updateCombo.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(final SelectionEvent e) {
final int selectionIndex= TickUpdateCellEditDialog.this.updateCombo.getSelectionIndex();
if (TickUpdateCellEditDialog.this.useAdjustBy) {
switch (selectionIndex) {
case 1:
TickUpdateCellEditDialog.this.editType= EditTypeEnum.ADJUST;
break;
}
} else {
switch (selectionIndex) {
case 1:
TickUpdateCellEditDialog.this.editType= EditTypeEnum.INCREASE;
break;
case 2:
TickUpdateCellEditDialog.this.editType= EditTypeEnum.DECREASE;
break;
}
}
}
});
this.updateCombo.select(0);
GridDataFactory.swtDefaults().applyTo(this.updateCombo);
}
@Override
public EditTypeEnum getEditType() {
return this.editType;
}
@Override
public Object calculateValue(final Object currentValue, final Object processValue) {
final double delta= processValue instanceof Number
? ((Number)processValue).doubleValue() : Double.valueOf((String)processValue).doubleValue();
if (this.editType == EditTypeEnum.ADJUST) {
if(delta >= 0) {
this.editType= EditTypeEnum.INCREASE;
} else {
this.editType= EditTypeEnum.DECREASE;
}
}
Object newValue= null;
switch (this.editType) {
case INCREASE:
newValue= this.tickUpdateHandler.getIncrementedValue(currentValue, delta);
break;
case DECREASE:
newValue= this.tickUpdateHandler.getDecrementedValue(currentValue, delta);
break;
}
try {
if (this.validator.validate(this.cell, this.configRegistry, newValue)) {
return newValue;
}
else {
WaLTablePlugin.log(new Status(IStatus.WARNING, WaLTablePlugin.BUNDLE_ID,
"Tick update failed for value " + newValue //$NON-NLS-1$
+ ". New value is not valid!" )); //$NON-NLS-1$
}
}
catch (final Exception e) {
WaLTablePlugin.log(new Status(IStatus.WARNING, WaLTablePlugin.BUNDLE_ID,
"Tick update failed for value " + newValue //$NON-NLS-1$
+ ". " + e.getLocalizedMessage() )); //$NON-NLS-1$
}
//return the unprocessed value because after the tick the value is invalid
return currentValue;
}
/**
* {@link IDataValidator} wrapper that is only used for tick update handling
* within this dialog. Will only execute the validation if {@link EditTypeEnum#SET}
* is used. Otherwise validation is skipped.
*/
private final class TickUpdateDataValidatorWrapper implements IDataValidator {
IDataValidator wrappedValidator;
TickUpdateDataValidatorWrapper(final IDataValidator wrappedValidator) {
this.wrappedValidator= wrappedValidator;
}
@Override
public boolean validate(final long columnIndex, final long rowIndex, final Object newValue) {
if (TickUpdateCellEditDialog.this.editType == EditTypeEnum.SET) {
return this.wrappedValidator.validate(columnIndex, rowIndex, newValue);
}
return true;
}
@Override
public boolean validate(final ILayerCell cell, final IConfigRegistry configRegistry, final Object newValue) {
if (TickUpdateCellEditDialog.this.editType == EditTypeEnum.SET) {
return this.wrappedValidator.validate(cell, configRegistry, newValue);
}
return true;
}
}
}