/******************************************************************************* * Copyright (c) 2005, 2007 IBM Corporation 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: * IBM Corporation - initial API and implementation * Brad Reynolds (bug 135446) * Brad Reynolds - bug 164653 *******************************************************************************/ package org.eclipse.ufacekit.ui.swing.databinding.internal.swing; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.util.EventListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import org.eclipse.core.databinding.observable.Diffs; import org.eclipse.core.databinding.observable.IObservable; import org.eclipse.core.databinding.observable.Realm; import org.eclipse.ufacekit.ui.swing.databinding.swing.SwingEventConstants; import org.eclipse.ufacekit.ui.swing.databinding.swing.SwingObservables; /** * {@link IObservable} implementation that wraps a {@link JTextComponent} * widget. 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> * * @since 1.0 */ public class TextObservableValue extends AbstractSwingVetoableValue { /** * {@link JTextComponent} widget that this is being observed. */ private final JTextComponent 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; /** * Valid types for the {@link #updateEventType}. */ private static final int[] validUpdateEventTypes = new int[] { SwingEventConstants.Modify, SwingEventConstants.FocusOut, SwingEventConstants.None }; /** * Previous value of the Text. */ private String oldValue; private EventListener updateListener = null; /** * Constructs a new instance bound to the given <code>text</code> widget * and configured to fire change events to its listeners at the time of the * <code>updateEventType</code>. * * @param text * @param updateEventType * SWT event constant as to what SWT event to update the model in * response to. Appropriate values are: <code>SWT.Modify</code>, * <code>SWT.FocusOut</code>, <code>SWT.None</code>. * @throws IllegalArgumentException * if <code>updateEventType</code> is an incorrect type. */ public TextObservableValue(final JTextComponent text, int updateEventType) { this(SwingObservables.getRealm(Thread.currentThread()), text, updateEventType); } /** * Constructs a new instance. * * @param realm * can not be <code>null</code> * @param text * @param updateEventType */ public TextObservableValue(final Realm realm, JTextComponent text, int updateEventType) { super(realm, text); 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 = text; if (updateEventType == SwingEventConstants.FocusOut) { this.updateListener = createFocusListener(); this.text.addFocusListener((FocusListener) updateListener); } else { this.updateListener = createDocumentListener(); Document document = this.text.getDocument(); document.putProperty("__DocumentListener__", //$NON-NLS-1$ updateListener); document.addDocumentListener((DocumentListener)updateListener); } oldValue = text.getText(); } private FocusListener createFocusListener() { return new FocusAdapter() { public void focusLost(FocusEvent e) { if (!updating) { String newValue = text.getText(); if (!newValue.equals(oldValue)) { fireValueChange(Diffs.createValueDiff(oldValue, newValue)); oldValue = newValue; } } } }; } private DocumentListener createDocumentListener() { return new DocumentListener() { public void changedUpdate(DocumentEvent documentevent) { } public void insertUpdate(DocumentEvent documentevent) { if (!updating) { String newValue = text.getText(); if (!newValue.equals(oldValue)) { fireValueChange(Diffs.createValueDiff(oldValue, newValue)); oldValue = newValue; } } } public void removeUpdate(DocumentEvent documentevent) { String newValue = text.getText(); if (!updating) { if (!newValue.equals(oldValue)) { fireValueChange(Diffs.createValueDiff(oldValue, newValue)); oldValue = newValue; } } } }; } /** * Sets the bound {@link JTextComponent 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 */ protected void doSetApprovedValue(final Object value) { try { updating = true; text.setText(value == null ? "" : value.toString()); //$NON-NLS-1$ oldValue = text.getText(); } finally { updating = false; } } /** * Returns the current value of the {@link JTextComponent}. * * @see org.eclipse.core.databinding.observable.value.AbstractVetoableValue#doGetValue() */ public Object doGetValue() { return oldValue = text.getText(); } /** * Returns the type of the value from {@link #doGetValue()}, i.e. * String.class * * @see org.eclipse.core.databinding.observable.value.IObservableValue#getValueType() */ public Object getValueType() { return String.class; } public void dispose() { if (updateListener != null) { if (updateListener instanceof FocusListener) this.text.removeFocusListener((FocusListener) updateListener); else this.text.getDocument().removeDocumentListener((DocumentListener) updateListener); } // if (!text.isDisposed()) { // if (updateEventType != SWT.None) { // text.removeListener(updateEventType, updateListener); // } // text.removeVerifyListener(verifyListener); // } super.dispose(); } }