org.polymap.rhei.field.PicklistFormField.java Source code

Java tutorial

Introduction

Here is the source code for org.polymap.rhei.field.PicklistFormField.java

Source

/* 
 * polymap.org
 * Copyright 2010-2013, Falko Brutigam. All rigths reserved.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 */
package org.polymap.rhei.field;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;

import org.polymap.rhei.form.IFormToolkit;
import org.polymap.rhei.model.ConstantWithSynonyms;

/**
 * 
 * <p/>
 * By default the {@link #setForceTextMatch(boolean)} is set to true, and
 * {@link #setTextEditable(boolean)} is set to false.
 * 
 * @author <a href="http://www.polymap.de">Falko Brutigam</a>
 */
public class PicklistFormField implements IFormField {

    private static Log log = LogFactory.getLog(PicklistFormField.class);

    /** */
    public static final int FORCE_MATCH = 1;

    /** */
    public static final int TEXT_EDITABLE = 2;

    private IFormFieldSite site;

    private Combo combo;

    private boolean textEditable = false;

    private boolean forceTextMatch = true;

    private LabelProvider labelProvider = new LabelProvider();

    /**
     * Maps display value into associated return code (when selected). The TreeMap
     * sorts tha keys alphabetically.
     */
    private final ValueProvider values;

    private Object loadedValue;

    private List<ModifyListener> modifyListeners = new ArrayList<ModifyListener>();

    /**
     * 
     */
    public static class LabelProvider {
        public String getText(String label, Object value) {
            return label;
        }
    }

    /**
     * A additional {@link LabelProvider} allows to transform the labels in the
     * dropdown of the combo of this picklist.
     */
    public void setLabelProvider(LabelProvider labelProvider) {
        this.labelProvider = labelProvider;
    }

    /**
     * 
     * 
     * @param flags {@link #TEXT_EDITABLE} or {@link #FORCE_MATCH}, or empty to use default settings. 
     */
    public PicklistFormField(int... flags) {
        this.values = new DefaultValueProvider();
        if (flags.length > 0) {
            setTextEditable(ArrayUtils.contains(flags, TEXT_EDITABLE));
            setForceTextMatch(ArrayUtils.contains(flags, FORCE_MATCH));
        }
    }

    /**
     * The given strings are used as label in the combo and as value to
     * be stored in the property.
     *  
     * @param values
     */
    public PicklistFormField(Iterable<String> values, int... flags) {
        this(flags);
        for (String value : values) {
            this.values.get().put(value, value);
        }
    }

    /**
     * The given strings are used as label in the combo and as value to
     * be stored in the property.
     *  
     * @param values
     */
    public PicklistFormField(String[] values, int... flags) {
        this(flags);
        for (String value : values) {
            this.values.get().put(value, value);
        }
    }

    /**
     *  
     * @param values Maps labels into property values
     */
    public PicklistFormField(Map<String, ? extends Object> values) {
        setForceTextMatch(true);
        setTextEditable(false);
        this.values = new DefaultValueProvider();
        this.values.get().putAll(values);
    }

    public PicklistFormField(ValueProvider valueProvider) {
        setForceTextMatch(true);
        setTextEditable(false);
        this.values = valueProvider;
    }

    public PicklistFormField(ConstantWithSynonyms.Type<? extends ConstantWithSynonyms, String> constants) {
        setForceTextMatch(true);
        setTextEditable(false);
        this.values = new DefaultValueProvider();
        for (ConstantWithSynonyms constant : constants) {
            this.values.get().put((String) constant.label, constant.id);
        }
    }

    public void init(IFormFieldSite _site) {
        this.site = _site;
    }

    public void dispose() {
        combo.dispose();
    }

    /**
     * Add a raw {@link ModifyListener} to the combo of this picklist. This
     * listener is called when the user types into the textfield of the combo.
     */
    public void addModifyListener(ModifyListener l) {
        modifyListeners.add(l);
        if (combo != null) {
            combo.addModifyListener(l);
        }
    }

    /**
     * Sets the text of the combo editable.
     * <p>
     * This method can be called only while initializing before the widget has
     * been created.
     * @return this
     */
    public PicklistFormField setTextEditable(boolean textEditable) {
        this.textEditable = textEditable;
        return this;
    }

    /**
     * If true, then the current text of the {@link #combo} is returned only if
     * it matches one of the labels. Otherwise the text is returned as is.
     * @return this
     */
    public PicklistFormField setForceTextMatch(boolean forceTextMatch) {
        this.forceTextMatch = forceTextMatch;
        return this;
    }

    public Control createControl(Composite parent, IFormToolkit toolkit) {
        int comboStyle = SWT.DROP_DOWN;
        comboStyle = !textEditable ? comboStyle | SWT.READ_ONLY : comboStyle & ~SWT.READ_ONLY;
        combo = toolkit.createCombo(parent, Collections.EMPTY_SET, comboStyle | SWT.MULTI);

        //
        for (ModifyListener l : modifyListeners) {
            combo.addModifyListener(l);
        }

        // add values
        fillCombo();

        // modify listener
        combo.addModifyListener(new ModifyListener() {
            public void modifyText(ModifyEvent ev) {
                Object value = getValue();
                log.debug("modifyEvent(): combo= " + combo.getText() + ", value= " + value);

                if (forceTextMatch && combo.getText().length() > 0 && value == null) {
                    site.setErrorMessage("Wert entspricht keiner der Vorgaben: " + combo.getText());
                } else {
                    site.setErrorMessage(null);
                }
                // null ist not allowed as text of the combo, so if the loadedValue was null,
                // then map "" value to null; otherwise the world would see that value as changed
                if (value != null && value.equals("") && loadedValue == null) {
                    value = null;
                }
                site.fireEvent(PicklistFormField.this, IFormFieldListener.VALUE_CHANGE, value);
            }
        });
        // selection listener
        combo.addSelectionListener(new SelectionListener() {
            public void widgetSelected(SelectionEvent ev) {
                log.debug("widgetSelected(): selectionIndex= " + combo.getSelectionIndex());

                int i = 0;
                for (String label : values.get().keySet()) {
                    if (i++ == combo.getSelectionIndex()) {
                        combo.setText(label);
                        break;
                    }
                }
                Object value = values.get().get(combo.getText());
                site.fireEvent(PicklistFormField.this, IFormFieldListener.VALUE_CHANGE, value);
            }

            public void widgetDefaultSelected(SelectionEvent ev) {
            }
        });

        // focus listener
        combo.addFocusListener(new FocusListener() {
            public void focusLost(FocusEvent event) {
                //                combo.setBackground( FormEditorToolkit.textBackground );
                site.fireEvent(this, IFormFieldListener.FOCUS_LOST, combo.getText());
            }

            public void focusGained(FocusEvent event) {
                //                combo.setBackground( FormEditorToolkit.textBackgroundFocused );
                site.fireEvent(this, IFormFieldListener.FOCUS_GAINED, combo.getText());
            }
        });

        return combo;
    }

    private void fillCombo() {
        for (Map.Entry<String, Object> entry : values.get().entrySet()) {
            combo.add(labelProvider.getText(entry.getKey(), entry.getValue()));
        }
    }

    public IFormField setEnabled(boolean enabled) {
        combo.setEnabled(enabled);
        return this;
    }

    public IFormField setValue(Object value) {
        combo.setText("");
        combo.deselectAll();

        if (forceTextMatch && value != null) {
            // find label for given value
            for (Map.Entry<String, Object> entry : values.get().entrySet()) {
                if (value.equals(entry.getValue())) {
                    combo.setText(entry.getKey());
                    break;
                }
            }
        } else {
            combo.setText(value != null ? (String) value : "");
        }
        return this;
    }

    /**
     * Returns the current value depending on the {@link #forceTextMatch} flag.
     * If true, then the current text of the {@link #combo} is returned only if
     * it matches one of the labels. Otherwise the text is returned as is.
     * 
     * @return
     */
    protected Object getValue() {
        String text = combo.getText();
        return forceTextMatch ? values.get().get(text) : text;
    }

    public void load() throws Exception {
        assert combo != null : "Control is null, call createControl() first.";

        loadedValue = site.getFieldValue();
        setValue(loadedValue);

        //        int i = 0;
        //        for (Iterator it=values.values().iterator(); it.hasNext(); i++) {
        //            if (it.next().equals( loadedValue )) {
        //                combo.select( i );
        //                return;
        //            }
        //        }
    }

    public void store() throws Exception {
        site.setFieldValue(getValue());
    }

    public void reloadValues() {
        if (combo == null) {
            throw new IllegalStateException("createControl must be called before");
        }
        combo.removeAll();
        fillCombo();
    }

    public interface ValueProvider {
        SortedMap<String, Object> get();
    }

    public static class DefaultValueProvider implements ValueProvider {

        private final SortedMap<String, Object> values = new TreeMap<String, Object>();

        @Override
        public SortedMap<String, Object> get() {
            return values;
        }
    }
}