/* * Copyright (c) 2012 Data Harmonisation Panel * * All rights reserved. This program and the accompanying materials are made * available under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * You should have received a copy of the GNU Lesser General Public License * along with this distribution. If not, see <http://www.gnu.org/licenses/>. * * Contributors: * HUMBOLDT EU Integrated Project #030962 * Data Harmonisation Panel <http://www.dhpanel.eu> */ package eu.esdihumboldt.hale.ui.functions.custom.pages.internal; import java.util.ArrayList; import java.util.Collections; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; import java.util.Observable; import javax.annotation.Nullable; import org.eclipse.jface.fieldassist.ControlDecoration; import org.eclipse.jface.fieldassist.FieldDecoration; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; import org.eclipse.jface.layout.GridDataFactory; import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.jface.util.IPropertyChangeListener; import org.eclipse.jface.util.PropertyChangeEvent; import org.eclipse.swt.SWT; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import eu.esdihumboldt.hale.ui.common.CommonSharedImages; import eu.esdihumboldt.hale.ui.common.Editor; /** * Represents a list of editable parameters. * * @param <T> the type of object to edit * @param <C> the editor control type for an object * @author Simon Templer */ public abstract class AbstractValueList<T, C extends Editor<T>> extends Observable { private class EditorWrapper<X> { private final C editor; private final Control mainControl; public EditorWrapper(C editor, Control mainControl) { super(); this.editor = editor; this.mainControl = mainControl; } public C getEditor() { return editor; } public Control getMainControl() { return mainControl; } } private final List<C> editors = new ArrayList<C>(); private final Map<C, EditorWrapper<C>> wrappers = new IdentityHashMap<>(); private final Composite editorContainer; private final boolean valid = false; private final IPropertyChangeListener propertyChangeListener; private Control addControl; /** * Create a parameter list. * * @param caption the list caption * @param description the list description * @param parent the parent composite * @param params the existing parameter values */ public AbstractValueList(@Nullable String caption, @Nullable String description, final Composite parent, List<T> params) { super(); ControlDecoration descriptionDecoration = null; // caption and description if (caption != null) { Label name = new Label(parent, SWT.NONE); name.setText(caption); name.setLayoutData(GridDataFactory.swtDefaults().create()); if (description != null) { // add decoration descriptionDecoration = new ControlDecoration(name, SWT.RIGHT, parent); } } editorContainer = new Composite(parent, SWT.NONE); editorContainer.setLayoutData(GridDataFactory.fillDefaults().grab(true, false).create()); // left margin 6 pixels for ControlDecorations to have place within this // component // so they're not drawn outside of the ScrolledComposite in case it's // present. editorContainer .setLayout(GridLayoutFactory.fillDefaults().extendedMargins(6, 0, 0, 0).create()); propertyChangeListener = new IPropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent event) { // TODO check event type? int changedIndex = editors.indexOf(event.getSource()); @SuppressWarnings("unused") C changedEditor = editors.get(changedIndex); // // add/remove selector // // check whether all selectors are valid (so must the changed // // one be) // if (countValidEntities() == editors.size()) { // // maybe last invalid entity was set, check whether to add // // another one // if (AbstractParameterList.this.definition.getMaxOccurrence() != editors.size()) { // S newSelector = createEditor(AbstractParameterList.this.ssid, // AbstractParameterList.this.definition, editorContainer); // newSelector.getControl().setLayoutData( // GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER) // .grab(true, false).create()); // addSelector(newSelector); // // // layout new selector in scrolled pane // editorContainer.getParent().getParent().layout(); // } // } // else { // // check whether a field was set to None and remove the // // field if it isn't the last one and minOccurrence is still // // met // if (event.getSelection().isEmpty() // && changedIndex != editors.size() - 1 // && AbstractParameterList.this.definition.getMinOccurrence() < editors // .size()) { // // check whether first selector will be removed and it // // had the fields description // boolean createDescriptionDecoration = changedIndex == 0 // && AbstractParameterList.this.definition.getDisplayName().isEmpty() // && !AbstractParameterList.this.definition.getDescription() // .isEmpty(); // removeSelector(changedEditor); // // // add new description decoration if necessary // if (createDescriptionDecoration) { // ControlDecoration descriptionDecoration = new ControlDecoration(editors // .get(0).getControl(), SWT.RIGHT | SWT.TOP, parent); // descriptionDecoration // .setDescriptionText(AbstractParameterList.this.definition // .getDescription()); // FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault() // .getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION); // descriptionDecoration.setImage(fieldDecoration.getImage()); // descriptionDecoration.setMarginWidth(2); // } // // // necessary layout call for control decoration to // // appear at the correct place // editorContainer.getParent().getParent().layout(); // // // add mandatory decoration to next selector if needed // if (changedIndex < AbstractParameterList.this.definition.getMinOccurrence()) { // S newMandatorySelector = editors // .get(AbstractParameterList.this.definition.getMinOccurrence() - 1); // // ControlDecoration mandatory = new ControlDecoration( // newMandatorySelector.getControl(), SWT.LEFT | SWT.TOP, parent); // FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault() // .getFieldDecoration(FieldDecorationRegistry.DEC_REQUIRED); // mandatory.setImage(CommonSharedImages.getImageRegistry().get( // CommonSharedImages.IMG_DECORATION_MANDATORY)); // mandatory.setDescriptionText(fieldDecoration.getDescription()); // } // } // } // update state updateState(); } }; // add initial fields if (params != null) { for (T param : params) { // create editor EditorWrapper<C> wrapper = createEditorWrapper(editorContainer, param); // add the editor now addEditor(wrapper); } } createAddControl(); // setup description decoration if (descriptionDecoration != null) { descriptionDecoration.setDescriptionText(description); FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault() .getFieldDecoration(FieldDecorationRegistry.DEC_INFORMATION); descriptionDecoration.setImage(fieldDecoration.getImage()); descriptionDecoration.setMarginWidth(2); } updateLayout(); updateState(); } @SuppressWarnings("javadoc") protected void updateLayout() { // layout new editor in scrolled pane // FIXME this is currently very specific! editorContainer.getParent().getParent().layout(); } private EditorWrapper<C> createEditorWrapper(Composite parent, T value) { if (addControl != null) { addControl.dispose(); addControl = null; } // create control that encompasses editor and remove button // Composite comp = new Composite(parent, SWT.NONE); Group comp = new Group(parent, SWT.NONE); GridLayoutFactory.swtDefaults().numColumns(2).applyTo(comp); // editor final C editor = createEditor(comp); editor.setValue(value); GridDataFactory.fillDefaults().grab(true, false).applyTo(editor.getControl()); // remove button Button remove = new Button(comp, SWT.PUSH); remove.setToolTipText("Remove"); remove.setImage(CommonSharedImages.getImageRegistry().get(CommonSharedImages.IMG_REMOVE)); GridDataFactory.swtDefaults().align(SWT.END, SWT.BEGINNING).applyTo(remove); remove.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { removeEditor(editor); } }); // readd add control after editor createAddControl(); // create wrapper comp.setLayoutData(GridDataFactory.swtDefaults().align(SWT.FILL, SWT.CENTER) .grab(true, false).create()); return new EditorWrapper<>(editor, comp); } private void createAddControl() { if (addControl != null) { return; } GridDataFactory addData = GridDataFactory.swtDefaults().align(SWT.BEGINNING, SWT.BEGINNING); Composite addComp = new Composite(editorContainer, SWT.NONE); addData.applyTo(addComp); GridLayoutFactory.swtDefaults().numColumns(1).applyTo(addComp); Button add = new Button(addComp, SWT.PUSH); addData.applyTo(add); add.setToolTipText("Add"); add.setImage(CommonSharedImages.getImageRegistry().get(CommonSharedImages.IMG_ADD)); add.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { // create editor EditorWrapper<C> wrapper = createEditorWrapper(editorContainer, null); // add the editor now addEditor(wrapper); updateLayout(); } }); addControl = addComp; } /** * Create an editor. * * @param parent the parent composite * @return the editor */ protected abstract C createEditor(Composite parent); /** * Get the editors. * * @return the list of editors. */ protected List<C> getEditors() { return Collections.unmodifiableList(editors); } /** * Add an editor. * * @param wrapper the editor wrapper of the editor to add */ protected void addEditor(EditorWrapper<C> wrapper) { editors.add(wrapper.getEditor()); wrappers.put(wrapper.getEditor(), wrapper); wrapper.getEditor().setPropertyChangeListener(propertyChangeListener); } /** * Remove an editor. * * @param editor the editor to remove */ protected void removeEditor(C editor) { editor.setPropertyChangeListener(null); editors.remove(editor); EditorWrapper<C> wrapper = wrappers.remove(editor); if (wrapper != null) { wrapper.getMainControl().dispose(); } else { editor.getControl().dispose(); } } // /** // * Counts valid entities. // * // * @return number of valid entities // */ // private int countValidEntities() { // int validCount = 0; // for (EntitySelector<F> selector : editors) { // if (!selector.getSelection().isEmpty()) // TODO improve condition // validCount++; // } // return validCount; // } /** * Updates the valid state */ private void updateState() { // boolean newValid = countValidEntities() >= definition.getMinOccurrence(); // boolean change = newValid != valid; // valid = newValid; // if (change) { // setChanged(); // notifyObservers(); // } } /** * Determines if the field is valid in its current configuration * * @return if the field is valid */ public boolean isValid() { return valid; } /** * @return the values contained in the list editor */ public List<T> getValues() { List<T> result = new ArrayList<>(); for (C editor : editors) { result.add(editor.getValue()); } return result; } }