blob: 9e609f2a441e343b3e31a4173a94f483e4bccb28 [file] [log] [blame]
/*******************************************************************************
* 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.edit.config;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.nebula.widgets.nattable.Messages;
import org.eclipse.nebula.widgets.nattable.data.convert.ConversionFailedException;
import org.eclipse.nebula.widgets.nattable.data.validate.ValidationFailedException;
import org.eclipse.nebula.widgets.nattable.edit.editor.AbstractEditErrorHandler;
import org.eclipse.nebula.widgets.nattable.edit.editor.ICellEditor;
import org.eclipse.nebula.widgets.nattable.edit.editor.IEditErrorHandler;
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer.MoveDirectionEnum;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
/**
* Strategy class for conversion/validation failures. If the entered value is
* not valid, a warning dialog with the corresponding error message will show
* up. The warning dialog gives the opportunity to discard the invalid input or
* change it, which will cause the editor to stay open. Only handles errors on
* commit.
*/
public class DialogErrorHandling extends AbstractEditErrorHandler {
/**
* The {@link ICellEditor} for which this {@link DialogErrorHandling} is
* activated. Needed so it is possible to operate on it dependent on the
* users choice.
*/
protected ICellEditor editor;
/**
* Flag to configure if this dialog allows to commit invalid data. This is
* necessary to implement cross validation in NatTable by using dialogs to
* tell the user what is wrong. By default this value is set to
* <code>false</code> as cross validation is not the default validation use
* case.
*/
protected boolean allowCommit = false;
/**
* The shell title that will be used if there is no conversion or validation
* shell title configured.
*/
private String failureShellTitle = "%DialogErrorHandlingStrategy.failureTitle"; //$NON-NLS-1$
/**
* The shell title that will be used in case this
* {@link DialogErrorHandling} is called to handle a
* {@link ConversionFailedException}.
*/
private String conversionFailureShellTitle = "%DialogErrorHandlingStrategy.conversionFailureTitle"; //$NON-NLS-1$
/**
* The shell title that will be used in case this
* {@link DialogErrorHandling} is called to handle a
* {@link ValidationFailedException}.
*/
private String validationFailureShellTitle = "%DialogErrorHandlingStrategy.validationFailureTitle"; //$NON-NLS-1$
/**
* The text on the button for changing the entered value.
*/
private String changeButtonLabel = "%DialogErrorHandlingStrategy.warningDialog.changeButton"; //$NON-NLS-1$
/**
* The text on the button to discard the entered value.
*/
private String discardButtonLabel = "%DialogErrorHandlingStrategy.warningDialog.discardButton"; //$NON-NLS-1$
/**
* The text on the button to commit the entered invalid value. Needed to
* support cross validation.
*/
private String commitButtonLabel = "%DialogErrorHandlingStrategy.warningDialog.commitButton"; //$NON-NLS-1$
/**
* Create a new {@link DialogErrorHandling} with no underlying
* {@link IEditErrorHandler} that does not support cross validation.
*/
public DialogErrorHandling() {
this(null, false);
}
/**
* Create a new {@link DialogErrorHandling} with no underlying
* {@link IEditErrorHandler} that gives the opportunity to configure the
* error handling for cross validation.
*
* @param allowCommit
* Flag to configure if this dialog allows to commit invalid
* data. If this parameter is set to <code>true</code>, an
* additional button for committing the invalid data will be
* provided within the dialog.
*/
public DialogErrorHandling(boolean allowCommit) {
this(null, allowCommit);
}
/**
* Create a new {@link DialogErrorHandling} using the given
* {@link IEditErrorHandler} as the underlying to allow chaining of error
* handling. Using this constructor there is no cross validation support.
*
* @param underlyingErrorHandler
* The underlying {@link IEditErrorHandler}
*/
public DialogErrorHandling(IEditErrorHandler underlyingErrorHandler) {
this(underlyingErrorHandler, false);
}
/**
* Create a new {@link DialogErrorHandling} using the given
* {@link IEditErrorHandler} as the underlying to allow chaining of error
* handling.
*
* @param underlyingErrorHandler
* The underlying {@link IEditErrorHandler}
* @param allowCommit
* Flag to configure if this dialog allows to commit invalid
* data. If this parameter is set to <code>true</code>, an
* additional button for committing the invalid data will be
* provided within the dialog.
*/
public DialogErrorHandling(IEditErrorHandler underlyingErrorHandler, boolean allowCommit) {
super(underlyingErrorHandler);
this.allowCommit = allowCommit;
}
/**
* {@inheritDoc} After the error is handled by its underlying
* {@link IEditErrorHandler}, a dialog will be opened showing the error
* message to the user, giving the opportunity to decide if the entered
* value should be discarded or if the editor should stay open so the value
* can be modified.
*/
@Override
public void displayError(ICellEditor cellEditor, Exception e) {
super.displayError(cellEditor, e);
this.editor = cellEditor;
String shellTitle = getFailureShellTitle();
if (e instanceof ConversionFailedException
&& !getConversionFailureShellTitle().isEmpty()) {
shellTitle = getConversionFailureShellTitle();
} else if (e instanceof ValidationFailedException
&& !getValidationFailureShellTitle().isEmpty()) {
shellTitle = getValidationFailureShellTitle();
}
showWarningDialog(e.getLocalizedMessage(), shellTitle);
}
/**
* Shows a warning dialog if the conversion or the validation returned an
* error message. Otherwise nothing happens.
*
* @param dialogMessage
* the dialog message
* @param dialogTitle
* the dialog title
*/
protected void showWarningDialog(String dialogMessage, String dialogTitle) {
if (!isWarningDialogActive() && dialogMessage != null) {
// conversion/validation failed - so open dialog with error message
String[] buttonLabels = this.allowCommit ? new String[] { getChangeButtonLabel(), getDiscardButtonLabel(), getCommitButtonLabel() }
: new String[] { getChangeButtonLabel(), getDiscardButtonLabel() };
MessageDialog warningDialog = new MessageDialog(
Display.getDefault().getActiveShell(),
dialogTitle,
null,
dialogMessage,
MessageDialog.WARNING,
buttonLabels,
0);
// if discard was selected close the editor
int returnCode = warningDialog.open();
if (returnCode == 1) {
this.editor.close();
}
// if commit was selected, commit the value by skipping the
// validation
else if (returnCode == 2) {
this.editor.commit(MoveDirectionEnum.NONE, true, true);
}
}
}
/**
* Checks if the current active Shell is a conversion/validation failure
* warning dialog. As a Shell has not id it is checked if the Shell title is
* for conversion or validation failure in localized format.
*
* @return <code>true</code> if a warning dialog is active
*/
protected boolean isWarningDialogActive() {
// check if the current active shell is a conversion or validation
// failure warning dialog
Shell control = Display.getDefault().getActiveShell();
return (control != null
&& (getConversionFailureShellTitle().equals(control.getText())
|| getValidationFailureShellTitle().equals(control.getText())
|| getFailureShellTitle().equals(control.getText())));
}
/**
* @return The shell title that will be used if there is no conversion or
* validation shell title configured.
*/
public String getFailureShellTitle() {
return getLocalized(this.failureShellTitle);
}
/**
* @param failureShellTitle
* The shell title that should be used if there is no conversion
* or validation shell title configured.
*/
public void setFailureShellTitle(String failureShellTitle) {
this.failureShellTitle = failureShellTitle;
}
/**
* @return The shell title that will be used in case this
* {@link DialogErrorHandling} is called to handle a
* {@link ConversionFailedException}.
*/
public String getConversionFailureShellTitle() {
return getLocalized(this.conversionFailureShellTitle);
}
/**
* @param conversionFailureShellTitle
* The shell title that should be used in case this
* {@link DialogErrorHandling} is called to handle a
* {@link ConversionFailedException}.
*/
public void setConversionFailureShellTitle(String conversionFailureShellTitle) {
this.conversionFailureShellTitle = conversionFailureShellTitle;
}
/**
* @return The shell title that will be used in case this
* {@link DialogErrorHandling} is called to handle a
* {@link ValidationFailedException}.
*/
public String getValidationFailureShellTitle() {
return getLocalized(this.validationFailureShellTitle);
}
/**
* @param validationFailureShellTitle
* The shell title that should be used in case this
* {@link DialogErrorHandling} is called to handle a
* {@link ValidationFailedException}.
*/
public void setValidationFailureShellTitle(String validationFailureShellTitle) {
this.validationFailureShellTitle = validationFailureShellTitle;
}
/**
* @return The text on the button for changing the entered value.
*/
public String getChangeButtonLabel() {
return getLocalized(this.changeButtonLabel);
}
/**
* @param changeButtonLabel
* The text on the button for changing the entered value.
*/
public void setChangeButtonLabel(String changeButtonLabel) {
this.changeButtonLabel = changeButtonLabel;
}
/**
* @return The text on the button to discard the entered value.
*/
public String getDiscardButtonLabel() {
return getLocalized(this.discardButtonLabel);
}
/**
* @param discardButtonLabel
* The text on the button to discard the entered value.
*/
public void setDiscardButtonLabel(String discardButtonLabel) {
this.discardButtonLabel = discardButtonLabel;
}
/**
* @return The text on the button to commit the entered value.
* @since 1.4
*/
public String getCommitButtonLabel() {
return getLocalized(this.commitButtonLabel);
}
/**
* @param commitButtonLabel
* The text on the button to commit the entered value.
* @since 1.4
*/
public void setCommitButtonLabel(String commitButtonLabel) {
this.commitButtonLabel = commitButtonLabel;
}
private String getLocalized(String text) {
return (text != null) ? Messages.getLocalizedMessage(text) : ""; //$NON-NLS-1$
}
}