diff options
Diffstat (limited to 'plugins/org.eclipse.eef.ide.ui/src/org/eclipse/eef/ide/ui/internal/widgets/EEFTextLifecycleManager.java')
-rw-r--r-- | plugins/org.eclipse.eef.ide.ui/src/org/eclipse/eef/ide/ui/internal/widgets/EEFTextLifecycleManager.java | 161 |
1 files changed, 114 insertions, 47 deletions
diff --git a/plugins/org.eclipse.eef.ide.ui/src/org/eclipse/eef/ide/ui/internal/widgets/EEFTextLifecycleManager.java b/plugins/org.eclipse.eef.ide.ui/src/org/eclipse/eef/ide/ui/internal/widgets/EEFTextLifecycleManager.java index 1560e0d56..9c5994c58 100644 --- a/plugins/org.eclipse.eef.ide.ui/src/org/eclipse/eef/ide/ui/internal/widgets/EEFTextLifecycleManager.java +++ b/plugins/org.eclipse.eef.ide.ui/src/org/eclipse/eef/ide/ui/internal/widgets/EEFTextLifecycleManager.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015, 2016 Obeo. + * Copyright (c) 2015, 2017 Obeo. * 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 @@ -31,6 +31,8 @@ import org.eclipse.eef.ide.ui.api.widgets.AbstractEEFWidgetLifecycleManager; import org.eclipse.eef.ide.ui.api.widgets.EEFStyleHelper; import org.eclipse.eef.ide.ui.api.widgets.EEFStyleHelper.IEEFTextStyleCallback; import org.eclipse.eef.ide.ui.internal.EEFIdeUiPlugin; +import org.eclipse.eef.ide.ui.internal.Messages; +import org.eclipse.eef.ide.ui.internal.preferences.EEFPreferences; import org.eclipse.eef.ide.ui.internal.widgets.styles.EEFColor; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.util.EcoreUtil; @@ -46,9 +48,11 @@ import org.eclipse.swt.events.KeyListener; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Widget; import org.eclipse.ui.forms.widgets.FormToolkit; @@ -58,6 +62,25 @@ import org.eclipse.ui.forms.widgets.FormToolkit; * @author sbegaudeau */ public class EEFTextLifecycleManager extends AbstractEEFWidgetLifecycleManager { + /** + * The different ways an edition conflict can be resolved. Used by the default implementation of + * {@link EEFTextLifecycleManager#resolveEditionConflict(Shell, String, String, String)}. + */ + public static enum ConflictResolutionMode { + /** + * Use the version being edited in the widget, overriding the new version computed from the current model state. + */ + USE_LOCAL_VERSION, + /** + * Use the version computed from the current model state, replacing the text being edited by the user in the + * widget. + */ + USE_MODEL_VERSION, + /** + * Ask the user through a simple dialog which version to keep. + */ + ASK_USER + } /** * This constant is used in order to tell SWT that the text area should be 300px wide even if it is not useful. The @@ -67,12 +90,6 @@ public class EEFTextLifecycleManager extends AbstractEEFWidgetLifecycleManager { private static final int TEXT_AREA_WIDTH_HINT = 300; /** - * Feature flag to enable experimental workaround for the text recovery in case of concurrent refresh. - */ - private static final boolean FLAG_RECOVER_TEXT_INPUT = "true" //$NON-NLS-1$ - .equals(System.getProperty("org.eclipse.eef.experimental.recoverLostTextInput", "false")); //$NON-NLS-1$ //$NON-NLS-2$ - - /** * The description. */ private EEFTextDescription description; @@ -318,15 +335,9 @@ public class EEFTextLifecycleManager extends AbstractEEFWidgetLifecycleManager { this.focusListener = new FocusListener() { @Override public void focusLost(FocusEvent e) { - if (FLAG_RECOVER_TEXT_INPUT) { - if (!EEFTextLifecycleManager.this.lockedByOtherInProgress.get() && !EEFTextLifecycleManager.this.container.isRenderingInProgress() - && EEFTextLifecycleManager.this.isDirty) { - EEFTextLifecycleManager.this.updateValue(false); - } - } else { - if (!EEFTextLifecycleManager.this.container.isRenderingInProgress() && EEFTextLifecycleManager.this.isDirty) { - EEFTextLifecycleManager.this.updateValue(false); - } + if (!EEFTextLifecycleManager.this.lockedByOtherInProgress.get() && !EEFTextLifecycleManager.this.container.isRenderingInProgress() + && EEFTextLifecycleManager.this.isDirty) { + EEFTextLifecycleManager.this.updateValue(false); } } @@ -389,32 +400,14 @@ public class EEFTextLifecycleManager extends AbstractEEFWidgetLifecycleManager { if (m != null) { boolean resettingToPreviousReferenceValue = equals(newDisplayText[0], m.referenceValue); boolean userHasUncommitedInput = !equals(newDisplayText[0], m.userInput); - if (FLAG_RECOVER_TEXT_INPUT && m.appliesTo(EEFTextLifecycleManager.this) && userHasUncommitedInput) { + if (m.appliesTo(EEFTextLifecycleManager.this) && userHasUncommitedInput) { if (resettingToPreviousReferenceValue) { // Custom user input overrides resetting the same previous referenceValue. newDisplayText[0] = m.userInput; - } else { - // The new model state produces a different value than the one the user saw when he - // started - // editing. - String msg = "The model has changed in an incompatible way while you were editing this widget.\nNew value from the model: {0}\nEdited version: {1}"; //$NON-NLS-1$ - String[] choices = { "Use new value from the model", "Keep the edited version" }; //$NON-NLS-1$ //$NON-NLS-2$ - // Can not use MessageDialog.open(kind, parent, title, message, style, - // dialogButtonLabels) - // with Neon... - MessageDialog dialog = new MessageDialog(EEFTextLifecycleManager.this.text.getShell(), "Model Changed During Edition", //$NON-NLS-1$ - null, MessageFormat.format(msg, newDisplayText[0], m.userInput), MessageDialog.QUESTION, 0, choices); - switch (dialog.open()) { - case 0: - // Nothing to do, newDisplayText[0] is initialized with the value from the model. - break; - case 1: - // Override with user input. - newDisplayText[0] = m.userInput; - break; - default: - throw new IllegalStateException(); - } + } else if (!equals(m.userInput, newDisplayText[0])) { + // Conflict must be resolved somehow. + newDisplayText[0] = resolveEditionConflict(EEFTextLifecycleManager.this.text.getShell(), m.referenceValue, m.userInput, + newDisplayText[0]); } } Memento.remove(text); @@ -423,6 +416,84 @@ public class EEFTextLifecycleManager extends AbstractEEFWidgetLifecycleManager { } /** + * Handle conflicts between un-commited changes in the widget and concurrent changes in the model that produce a + * different value than the original one seen by the user. + * + * @param shell + * the shell to use if user interaction is needed. + * @param originalValue + * the original, common value, before the user started editing and before the concurrent model change + * produced a new text. + * @param localEditedVersion + * the value as edited by the user, and seen in the UI. + * @param newValueFromModel + * the new value produced from the new model state. + * @return the new value to use in the text field. + */ + protected String resolveEditionConflict(Shell shell, String originalValue, String localEditedVersion, final String newValueFromModel) { + String result; + switch (EEFPreferences.getTextConflictResolutionMode()) { + case USE_LOCAL_VERSION: + result = localEditedVersion; + break; + case USE_MODEL_VERSION: + result = newValueFromModel; + break; + case ASK_USER: + result = askUserToResolveConflict(shell, originalValue, localEditedVersion, newValueFromModel); + break; + default: + throw new IllegalStateException(); + } + return result; + } + + /** + * Open a simple dialog to inform the user of a conflict and ask him which version to keep. + * + * @param shell + * the shell to use if user interaction is needed. + * @param originalValue + * the original, common value, before the user started editing and before the concurrent model change + * produced a new text. + * @param localEditedVersion + * the value as edited by the user, and seen in the UI. + * @param newValueFromModel + * the new value produced from the new model state. + * @return the value chosen by the user. + */ + protected String askUserToResolveConflict(final Shell shell, String originalValue, final String localEditedVersion, + final String newValueFromModel) { + final String[] result = { localEditedVersion }; + // @formatter:off + final String[] choices = { + Messages.EEFTextLifecycleManager_conflictDialog_choiceNewModelValue, + Messages.EEFTextLifecycleManager_conflictDialog_choiceLocalEditedValue, + }; + // @formatter:on + shell.getDisplay().syncExec(new Runnable() { + @Override + public void run() { + Image img = shell.getDisplay().getSystemImage(SWT.ICON_QUESTION); + MessageDialog dialog = new MessageDialog(shell, Messages.EEFTextLifecycleManager_conflictDialog_title, img, + MessageFormat.format(Messages.EEFTextLifecycleManager_conflictDialog_message, newValueFromModel, localEditedVersion), + MessageDialog.QUESTION, 0, choices); + switch (dialog.open()) { + case 0: + result[0] = newValueFromModel; + break; + case 1: + result[0] = localEditedVersion; + break; + default: + throw new IllegalStateException(); + } + } + }); + return result[0]; + } + + /** * Tests two objects for equality, considering <code>null</code>. * * @param o1 @@ -516,15 +587,11 @@ public class EEFTextLifecycleManager extends AbstractEEFWidgetLifecycleManager { @Override protected void lockedByOther() { - if (FLAG_RECOVER_TEXT_INPUT) { - this.lockedByOtherInProgress.set(true); - try { - super.lockedByOther(); - } finally { - this.lockedByOtherInProgress.set(false); - } - } else { + this.lockedByOtherInProgress.set(true); + try { super.lockedByOther(); + } finally { + this.lockedByOtherInProgress.set(false); } } |