| Summary: | [DataBinding] databinding needs support for editors with undoable commands | ||
|---|---|---|---|
| Product: | [Eclipse Project] Platform | Reporter: | Michael Scharf <eclipse> |
| Component: | UI | Assignee: | Platform-UI-Inbox <Platform-UI-Inbox> |
| Status: | RESOLVED DUPLICATE | QA Contact: | |
| Severity: | normal | ||
| Priority: | P3 | CC: | djo, gunnar |
| Version: | 3.2 | ||
| Target Milestone: | --- | ||
| Hardware: | PC | ||
| OS: | All | ||
| Whiteboard: | |||
This is a dupe of bug #116465. I'd resolve the bug but for some reason I can't... Thanks for the comments, Michael! I've copied them over to bug #116465, where we're tracking the undo/redo issue. |
I have created a data binding framework that binds EMF with SWT widgets similar to the jface.databinding. I had to solve a problem that seems not to be addressed in the jface.databinding (at least I did not find an obvious solution). It has to do with undo-redo support: Let's say, I want to bind a text field with a swt.Text widget. My EMF binding creates undoable commands for every change. I have two choices how to update the model: either update it on every keystroke or update it when the user leaves the text field or hits return. The first approach creates too many commands on the command stack. The second approach works fine, except for one case: when the file has not been edited and the user starts typing into the text field. In this case the editor does not know that the file has been modified (it usually observes the command stack), and therefore the save action is disabled. I solved the problem by registering two bindings for the text: one that listens to SWT.ValueChanged (to set the value in the model) and one that listens to SWT.Modify (to tell the editor, that the save button needs to be enabled, the Text field notifies the editor that is has "pending changes"). The editor also needs a way to tell the Text to set its value to the model, before the save is done (else the pending changes are not saved). That is simple: use Text.getText(). However the editor should not know all its Text fields, therefore the framework should to support it. To deal with that, I created a class CommandSupport (see below). It allows to register two types of listeners. Editors register a PendingChangesListener to get notified if a value changes. Text fields register a FlushPendingChangesListener to get notified before the editor wants to save (to put the pending changes into the model). The FlushPendingChangesListener has also a method hasPendingChanges() so that the Editor can figure out, if there are pending changes. In my framework, the equivalent to IUpdatable has a getCommandSupport() method: CommandSupport getCommandSupport(); It somehow belongs to the IDataBindingContext (my IUpdatable has also a method to access IDataBindingContext). But that's another topic... Here is my version of CommandSupport. I have updated a little bit, to make it compatible with jface.databinding. package org.eclipse.jface.databinding; /** * Text editors have their own undo stack. Text fields should * not update the model on every keystroke. However the editor * has to know if the model needs save or if an undo is allowed. * <p> * * Therefore the editor registers a * {@link CommandSupport.PendingChangesListener} * to get notified that there are pending changes. * * <p> * * {@link org.eclipse.jface.databinding.UpdatableValue} * that have pending changes register a * {@link CommandSupport.FlushPendingChangesListener} * to get notified when changes need to be flushed. * * */ public interface ICommandSupport { //---------------------------------------------------- // Support for Command Stacks //---------------------------------------------------- /** * @return true if there is a subeditor that is modified */ boolean hasPendingChanges(); /** * Listeners interested in changes of * {@link CommandSupport#hasPendingChanges()} * should register this interface */ public interface PendingChangesListener { /** * Tell the listener that the state of * {@link CommandSupport#hasPendingChanges()} * might have changed */ void pendingChangesChanged(); } /** * @param listener */ void addPendingChangesListener(PendingChangesListener listener); /** * @param listener */ void removePendingChangesListener(PendingChangesListener listener); /** * Called by a edit field that maintains a pending change. * This informs {@link PendingChangesListener} that the state * of pending Changes might have changed */ void pendingChangesChanged(); /** * Tell the {@link FlushPendingChangesListener} to flush their * pending changes. */ void flushPendingChanges(); //---------------------------------------------------- // Support for IUpdatableValue //---------------------------------------------------- /** * {@link org.eclipse.jface.databinding.IUpdatableValue} * that have pending changes should register a * FlushPendingChangesListener. * */ public interface FlushPendingChangesListener { /** * @return true if there is a pending change */ boolean hasPendingChanges(); /** * The owner of a pending change should put it into the model */ void flushPendingChanges(); } /** * @param listener */ void addFlushPendingChangesListener(FlushPendingChangesListener listener); /** * @param listener */ void removeFlushPendingChangesListener(FlushPendingChangesListener listener); }