| /*=============================================================================# |
| # Copyright (c) 2005, 2019 IBM Corporation 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: |
| # IBM Corporation - org.eclipse.jdt: initial API and implementation |
| # Stephan Wahlbrink <sw@wahlbrink.eu> - initial API and implementation |
| #=============================================================================*/ |
| |
| package org.eclipse.statet.ltk.ui.sourceediting; |
| |
| import org.eclipse.core.databinding.observable.Diffs; |
| import org.eclipse.core.databinding.observable.IObservable; |
| import org.eclipse.core.databinding.observable.Realm; |
| import org.eclipse.core.databinding.observable.value.AbstractVetoableValue; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.events.DisposeEvent; |
| import org.eclipse.swt.events.DisposeListener; |
| import org.eclipse.swt.events.VerifyEvent; |
| import org.eclipse.swt.events.VerifyListener; |
| import org.eclipse.swt.widgets.Event; |
| import org.eclipse.swt.widgets.Listener; |
| import org.eclipse.swt.widgets.Text; |
| |
| |
| /** |
| * {@link IObservable} implementation that wraps a {@link SnippetEditor}. The time at which |
| * listeners should be notified about changes to the text is specified on construction. |
| * |
| * <dl> |
| * <dt>Events:</dt> |
| * <dd>If the update event type (specified on construction) is <code>SWT.Modify</code> a value |
| * change event will be fired on every key stroke. If the update event type is |
| * <code>SWT.FocusOut</code> a value change event will be fired on focus out. When in either |
| * mode if the user is entering text and presses [Escape] the value will be reverted back to |
| * the last value set using doSetValue(). Regardless of the update event type a value |
| * changing event will fire on verify to enable vetoing of changes.</dd> |
| * </dl> |
| */ |
| public class SnippetEditorObservable extends AbstractVetoableValue { |
| |
| /** |
| * {@link Text} widget that this is being observed. |
| */ |
| private final StyledText text; |
| |
| /** |
| * Flag to track when the model is updating the widget. When |
| * <code>true</code> the handlers for the SWT events should not process |
| * the event as this would cause an infinite loop. |
| */ |
| private boolean updating= false; |
| |
| /** |
| * SWT event that on firing this observable will fire change events to its |
| * listeners. |
| */ |
| private final int updateEventType; |
| |
| /** |
| * Valid types for the {@link #updateEventType}. |
| */ |
| private static final int[] validUpdateEventTypes= new int[] { SWT.Modify, |
| SWT.FocusOut, SWT.None }; |
| |
| /** |
| * Previous value of the Text. |
| */ |
| private String oldValue; |
| |
| private final Listener updateListener= new Listener() { |
| @Override |
| public void handleEvent(final Event event) { |
| if (!SnippetEditorObservable.this.updating) { |
| final String newValue= SnippetEditorObservable.this.text.getText(); |
| |
| if (!newValue.equals(SnippetEditorObservable.this.oldValue)) { |
| fireValueChange(Diffs.createValueDiff(SnippetEditorObservable.this.oldValue, newValue)); |
| SnippetEditorObservable.this.oldValue= newValue; |
| } |
| } |
| } |
| }; |
| |
| private final VerifyListener verifyListener; |
| |
| |
| /** |
| * Constructs a new instance. |
| * |
| * @param realm can not be <code>null</code> |
| * @param text |
| * @param updateEventType |
| */ |
| public SnippetEditorObservable(final Realm realm, final SnippetEditor editor, |
| final int updateEventType) { |
| super(realm); |
| |
| boolean eventValid= false; |
| for (int i= 0; !eventValid && i < validUpdateEventTypes.length; i++) { |
| eventValid= (updateEventType == validUpdateEventTypes[i]); |
| } |
| if (!eventValid) { |
| throw new IllegalArgumentException( |
| "UpdateEventType [" + updateEventType + "] is not supported."); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| |
| this.text= editor.getSourceViewer().getTextWidget(); |
| this.updateEventType= updateEventType; |
| if (updateEventType != SWT.None) { |
| this.text.addListener(updateEventType, this.updateListener); |
| } |
| |
| this.oldValue= this.text.getText(); |
| |
| this.verifyListener= new VerifyListener() { |
| @Override |
| public void verifyText(final VerifyEvent e) { |
| if (!SnippetEditorObservable.this.updating) { |
| final String currentText= SnippetEditorObservable.this.text |
| .getText(); |
| final String newText= currentText.substring(0, e.start) + e.text |
| + currentText.substring(e.end); |
| if (!fireValueChanging(Diffs.createValueDiff(currentText, |
| newText))) { |
| e.doit= false; |
| } |
| } |
| } |
| }; |
| this.text.addVerifyListener(this.verifyListener); |
| |
| this.text.addDisposeListener(new DisposeListener(){ |
| @Override |
| public void widgetDisposed(final DisposeEvent e) { |
| SnippetEditorObservable.this.dispose(); |
| } |
| }); |
| } |
| |
| |
| /** |
| * Sets the bound {@link Text Text's} text to the passed <code>value</code>. |
| * |
| * @param value |
| * new value, String expected |
| * @see org.eclipse.core.databinding.observable.value.AbstractVetoableValue#doSetApprovedValue(java.lang.Object) |
| * @throws ClassCastException |
| * if the value is anything other than a String |
| */ |
| @Override |
| protected void doSetApprovedValue(final Object value) { |
| try { |
| this.updating= true; |
| this.text.setText(value == null ? "" : value.toString()); //$NON-NLS-1$ |
| this.oldValue= this.text.getText(); |
| } finally { |
| this.updating= false; |
| } |
| } |
| |
| /** |
| * Returns the current value of the {@link Text}. |
| * |
| * @see org.eclipse.core.databinding.observable.value.AbstractVetoableValue#doGetValue() |
| */ |
| @Override |
| public Object doGetValue() { |
| return this.oldValue= this.text.getText(); |
| } |
| |
| /** |
| * Returns the type of the value from {@link #doGetValue()}, i.e. |
| * String.class |
| * |
| * @see org.eclipse.core.databinding.observable.value.IObservableValue#getValueType() |
| */ |
| @Override |
| public Object getValueType() { |
| return String.class; |
| } |
| |
| @Override |
| public void dispose() { |
| if (!this.text.isDisposed()) { |
| if (this.updateEventType != SWT.None) { |
| this.text.removeListener(this.updateEventType, this.updateListener); |
| } |
| this.text.removeVerifyListener(this.verifyListener); |
| } |
| super.dispose(); |
| } |
| |
| } |