| /******************************************************************************* |
| * Copyright (c) 2013, 2020 Dirk Fauth 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: |
| * Dirk Fauth <dirk.fauth@googlemail.com> - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.nebula.widgets.nattable.edit.gui; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.eclipse.jface.layout.GridDataFactory; |
| import org.eclipse.nebula.widgets.nattable.Messages; |
| import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry; |
| import org.eclipse.nebula.widgets.nattable.data.validate.IDataValidator; |
| import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes; |
| import org.eclipse.nebula.widgets.nattable.edit.EditTypeEnum; |
| import org.eclipse.nebula.widgets.nattable.edit.editor.AbstractCellEditor; |
| import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor; |
| import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell; |
| import org.eclipse.nebula.widgets.nattable.style.DisplayMode; |
| import org.eclipse.nebula.widgets.nattable.tickupdate.ITickUpdateHandler; |
| import org.eclipse.nebula.widgets.nattable.tickupdate.TickUpdateConfigAttributes; |
| import org.eclipse.nebula.widgets.nattable.widget.EditModeEnum; |
| 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; |
| |
| /** |
| * 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 Log LOG = LogFactory.getLog(TickUpdateCellEditDialog.class); |
| |
| 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 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(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; |
| |
| Boolean useAdjustByConfig = configRegistry.getConfigAttribute( |
| TickUpdateConfigAttributes.USE_ADJUST_BY, |
| DisplayMode.EDIT, |
| cell.getConfigLabels()); |
| this.useAdjustBy = useAdjustByConfig != null |
| ? useAdjustByConfig |
| : false; |
| } |
| |
| @Override |
| protected Control createDialogArea(Composite parent) { |
| Composite panel = new Composite(parent, SWT.NONE); |
| GridDataFactory.fillDefaults().grab(true, true).applyTo(panel); |
| |
| GridLayout panelLayout = new GridLayout(2, false); |
| panelLayout.marginWidth = 8; |
| panel.setLayout(panelLayout); |
| |
| createUpdateCombo(panel); |
| |
| // activate the new editor |
| this.cellEditor.activateCell(panel, this.originalCanonicalValue, |
| EditModeEnum.DIALOG, this.cellEditHandler, this.cell, |
| this.configRegistry); |
| |
| this.validator = this.configRegistry.getConfigAttribute( |
| EditConfigAttributes.DATA_VALIDATOR, |
| DisplayMode.EDIT, |
| this.cell.getConfigLabels()); |
| |
| 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)); |
| } |
| |
| 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(Composite composite) { |
| this.updateCombo = new Combo(composite, SWT.READ_ONLY | SWT.DROP_DOWN | SWT.BORDER); |
| |
| for (String option : this.useAdjustBy ? OPTIONS_ADJUST : OPTIONS_DEFAULT) { |
| this.updateCombo.add(option); |
| } |
| |
| this.updateCombo.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| 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(Object currentValue, Object processValue) { |
| double delta = ((processValue == null) |
| ? 0 |
| : (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 { |
| LOG.warn("Tick update failed for value " + newValue //$NON-NLS-1$ |
| + ". New value is not valid!"); //$NON-NLS-1$ |
| } |
| } catch (Exception e) { |
| LOG.warn("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(IDataValidator wrappedValidator) { |
| this.wrappedValidator = wrappedValidator; |
| } |
| |
| @Override |
| public boolean validate(int columnIndex, int rowIndex, Object newValue) { |
| if (TickUpdateCellEditDialog.this.editType == EditTypeEnum.SET) { |
| return this.wrappedValidator.validate(columnIndex, rowIndex, newValue); |
| } |
| return true; |
| } |
| |
| @Override |
| public boolean validate(ILayerCell cell, IConfigRegistry configRegistry, Object newValue) { |
| if (TickUpdateCellEditDialog.this.editType == EditTypeEnum.SET) { |
| return this.wrappedValidator.validate(cell, configRegistry, newValue); |
| } |
| return true; |
| } |
| } |
| } |