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