Tuesday, September 09, 2008

An enhanced ListEditor implementation

As discussed in this article on Eclipse.org, the Eclipse FieldEditors make it easy to implement the preferences pages. The ListEditor is one of the many imlementations provided by Eclipse which allows editing of list like values. However it has a serious limitation. It only allows addition, deletion and reordering of the values. It does not allow editing of the values already in the list. The following subclass of ListEditor overcomes that limitation by providing the functionality of editing any one value in the list of values. To use it you have to subclass and implement the following additional abstract method:

/**
* The subclasses must override this to return the modified entry.
*
* @param original the new entry
* @return the modified entry. Return null to prevent modification.
*/
protected abstract String getModifiedEntry(String original);

One possible implementation could look something like this:

@Override
protected String getModifiedEntry(String original) {
InputDialog entryDialog = new InputDialog(
PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
"Edit entry", "Edit entry:", original, null);
if (entryDialog.open() == InputDialog.OK) {
return entryDialog.getValue();
}
return null;
}

In fact, I make use of this in my Path Tools Eclipse Plug-in to edit the custom commands for folders and files. Here is the screenshot:


Here is the full code:

package somepackage;

import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.preference.ListEditor;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.List;

/**
* This class extends {@link ListEditor} to enable editing of entries.
*
* @author Sandip V. Chitale
*
*/
public abstract class EntryModifiableListEditor extends ListEditor {

public EntryModifiableListEditor(String name, String labelText, Composite parent) {
super(name, labelText, parent);
}

/**
* The subclasses must override this to return the modified entry.
*
* @param original the new entry
* @return the modified entry. Return null to prevent modification.
*/
protected abstract String getModifiedEntry(String original);

private Button editButton;
private List commandListControl;

@Override
public Composite getButtonBoxControl(Composite parent) {
Composite buttonBoxControl = super.getButtonBoxControl(parent);
if (editButton == null) {
editButton = createPushButton(buttonBoxControl, "Edit..."); // TODO I18N
editButton.setEnabled(false);
editButton.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
if (commandListControl.getSelectionCount() == 1) {
String modified = getModifiedEntry(commandListControl.getSelection()[0]);
if (modified != null) {
int selectedIndex = commandListControl.getSelectionIndex();
commandListControl.remove(selectedIndex);
commandListControl.add(modified, selectedIndex);
}
}
}
});
buttonBoxControl.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent event) {
editButton = null;
}
});
}
return buttonBoxControl;
}

/**
* Helper method to create a push button.
*
* @param parent the parent control
* @param key the resource name used to supply the button's label text
* @return Button
*/
private Button createPushButton(Composite parent, String key) {
Button button = new Button(parent, SWT.PUSH);
button.setText(key);
button.setFont(parent.getFont());
GridData data = new GridData(GridData.FILL_HORIZONTAL);
int widthHint = convertHorizontalDLUsToPixels(button,
IDialogConstants.BUTTON_WIDTH);
data.widthHint = Math.max(widthHint, button.computeSize(SWT.DEFAULT,
SWT.DEFAULT, true).x);
button.setLayoutData(data);
return button;
}

@Override
public List getListControl(Composite parent) {
List listControl = super.getListControl(parent);
if (commandListControl == null) {
commandListControl = listControl;
commandListControl.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
editButton.setEnabled(commandListControl.getSelectionCount() == 1);
}
});
}
return listControl;
}

}

2 comments:

JP Moresmau said...

Hello, just to let you know that I was looking for exactly this, so I've taken your code to use in my open source plugin, if you don't mind. The plugin is EclipseFP (Haskell in Eclipse), home page is http://eclipsefp.sourceforge.net. I've copied the code as is, of course I left your name as the author and added a link to this page.

Thanks for the good work!!

Sandip Chitale said...

Hello JP Moresmau, Thanks. You are welcome to use the code for EntryModifiableListEditor. I have submitted a patch for this to Eclipse. Hope they accept it. BTW I have now implemented TableFieldEditor along the same lines that uses a table instead of a list. Please have a look at this blog entry: http://sandipchitale.blogspot.com/2010/09/updated-path-tools-eclipse-plug-in.html . The code for the TableFieldEditor is: http://code.google.com/p/pathtools/source/browse/trunk/PathTools/src/pathtools/TableFieldEditor.java