view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/LocationDistancePanel.java @ 6996:0f740919fa26

LocationDistancePanel error messages: do not imply calculation in the direction of kilometrage.
author Tom Gottfried <tom.gottfried@intevation.de>
date Thu, 12 Sep 2013 10:15:18 +0200
parents ea9eef426962
children 48d8dba891f6
line wrap: on
line source
/* Copyright (C) 2011, 2012, 2013 by Bundesanstalt für Gewässerkunde
 * Software engineering by Intevation GmbH
 *
 * This file is Free Software under the GNU AGPL (>=v3)
 * and comes with ABSOLUTELY NO WARRANTY! Check out the
 * documentation coming with Dive4Elements River for details.
 */

package org.dive4elements.river.client.client.ui;

import com.google.gwt.core.client.GWT;
import com.google.gwt.i18n.client.NumberFormat;

import com.smartgwt.client.data.AdvancedCriteria;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.data.Criterion;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.types.OperatorId;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.RadioGroupItem;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.StaticTextItem;
import com.smartgwt.client.widgets.form.fields.events.BlurEvent;
import com.smartgwt.client.widgets.form.fields.events.BlurHandler;
import com.smartgwt.client.widgets.form.fields.events.ChangeEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangeHandler;
import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.CellClickEvent;
import com.smartgwt.client.widgets.grid.events.CellClickHandler;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tab.Tab;
import com.smartgwt.client.widgets.tab.TabSet;
import com.smartgwt.client.widgets.tab.events.TabSelectedEvent;
import com.smartgwt.client.widgets.tab.events.TabSelectedHandler;

import org.dive4elements.river.client.client.Config;
import org.dive4elements.river.client.client.FLYSConstants;
import org.dive4elements.river.client.client.event.FilterHandler;
import org.dive4elements.river.client.client.event.RangeFilterEvent;
import org.dive4elements.river.client.client.event.StringFilterEvent;
import org.dive4elements.river.client.client.services.DistanceInfoService;
import org.dive4elements.river.client.client.services.DistanceInfoServiceAsync;
import org.dive4elements.river.client.client.ui.range.DistanceInfoDataSource;
import org.dive4elements.river.client.shared.model.ArtifactDescription;
import org.dive4elements.river.client.shared.model.Data;
import org.dive4elements.river.client.shared.model.DataItem;
import org.dive4elements.river.client.shared.model.DataList;
import org.dive4elements.river.client.shared.model.DefaultData;
import org.dive4elements.river.client.shared.model.DefaultDataItem;
import org.dive4elements.river.client.shared.model.DistanceInfoObject;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;


/**
 * This UIProvider creates a widget to enter locations or a distance.
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class LocationDistancePanel
extends      AbstractUIProvider
implements   ChangeHandler, BlurHandler, FilterHandler
{
    private static final long serialVersionUID = -10820092176039372L;

    /** The message class that provides i18n strings. */
    protected FLYSConstants MESSAGES = GWT.create(FLYSConstants.class);

    /** The DistanceInfoService used to retrieve locations about rivers. */
    protected DistanceInfoServiceAsync distanceInfoService =
        GWT.create(DistanceInfoService.class);

    public static final String FIELD_MODE = "mode";

    /** The constant name of the input field to enter the start of a distance.*/
    public static final String FIELD_FROM = "from";

    /** The constant name of the input field to enter the end of a distance.*/
    public static final String FIELD_TO = "to";

    /** The constant name of the input field to enter locations.*/
    public static final String FIELD_VALUE_LOCATION = "location";

    /** The constant name of the input field to enter distance.*/
    public static final String FIELD_VALUE_DISTANCE = "distance";

    /** The constant name of the input field to enter the step width of a
     * distance.*/
    public static final String FIELD_WIDTH = "width";

    public static final int WIDTH = 250;


    /** The radio group for input mode selection.*/
    protected DynamicForm mode;

    /** A container that will contain the location or the distance panel.*/
    protected HLayout container;

    /** The min value for a distance.*/
    protected double min;

    /** The max value for a distance.*/
    protected double max;

    /** The 'from' value entered in the distance mode.*/
    protected double from;

    /** The 'to' value entered in the distance mode.*/
    protected double to;

    /** The 'step' value entered in the distance mode.*/
    protected double step;

    /** The values entered in the location mode.*/
    protected double[] values;

    /** The input panel for locations. */
    protected DoubleArrayPanel locationPanel;

    /** The input panel for distances. */
    protected DoubleRangePanel distancePanel;

    /** The tab set containing the location and distance table. */
    protected TabSet inputTables;

    /** The distance table. */
    protected ListGrid distanceTable;

    /** The locations table. */
    protected ListGrid locationsTable;

    /** The locations table for distance input. */
    protected ListGrid locationDistanceTable;

    /** The table data. */
    protected DistanceInfoObject[] tableData;

    /** The table filter.*/
    protected TableFilter filterDescription;
    protected RangeTableFilter filterRange;

    /** The Combobox for table filter criteria. */
    protected SelectItem filterCriteria;
    protected StaticTextItem filterResultCount;
    protected ListGrid currentFiltered;

    /**
     * Creates a new LocationDistancePanel instance.
     */
    public LocationDistancePanel() {
        distanceTable  = new ListGrid();
        distanceTable.setAutoFetchData(true);

        locationsTable = new ListGrid();
        locationsTable.setAutoFetchData(true);

        locationDistanceTable = new ListGrid();
        locationDistanceTable.setAutoFetchData(true);

        locationDistanceTable.setShowHeaderContextMenu(false);
        distanceTable.setShowHeaderContextMenu(false);
        locationsTable.setShowHeaderContextMenu(false);
    }


    /**
     * This method creates a widget that contains a label, a panel with
     * checkboxes to switch the input mode between location and distance input,
     * and a the mode specific panel.
     *
     * @param data The data that might be inserted.
     *
     * @return a panel.
     */
    @Override
    public Canvas create(DataList data) {
        VLayout layout = new VLayout();
        layout.setMembersMargin(10);

        Label label   = new Label(MESSAGES.location_distance_state());
        Canvas widget = createWidget(data);
        Canvas submit = getNextButton();
        createDistanceInputPanel();

        initDefaults(data);

        createLocationTableDistance ();
        createDistanceTable();
        createLocationTable();

        widget.setHeight(50);
        label.setHeight(25);

        layout.addMember(label);
        layout.addMember(widget);
        layout.addMember(submit);

        return layout;
    }


    /**
     * This method creates a table that contains the distance values.
     */
    protected void createDistanceTable() {

        String baseUrl = GWT.getHostPageBaseURL();

        distanceTable.setWidth100();
        distanceTable.setShowRecordComponents(true);
        distanceTable.setShowRecordComponentsByCell(true);
        distanceTable.setHeight100();
        distanceTable.setEmptyMessage(MESSAGES.empty_filter());
        distanceTable.setCanReorderFields(false);

        CellFormatter cf = new CellFormatter() {
            @Override
            public String format(
                Object value,
                ListGridRecord record,
                int rowNum, int colNum) {
                    if (value == null) return null;
                    try {
                        NumberFormat nf;
                        double v = Double.parseDouble((String)value);
                        nf = NumberFormat.getFormat("###0.00##");
                        return nf.format(v);
                    }
                    catch (Exception e) {
                        return value.toString();
                    }
            }
        };

        ListGridField addDistance = new ListGridField ("", "");
        addDistance.setType (ListGridFieldType.ICON);
        addDistance.setWidth (20);
        addDistance.addRecordClickHandler (new RecordClickHandler () {
            @Override
            public void onRecordClick (RecordClickEvent e) {
                if (!isLocationMode ()) {
                    Record r = e.getRecord();
                    try {
                        double min = Double.parseDouble(r.getAttribute("from"));
                        double max = Double.parseDouble(r.getAttribute("to"));
                        setDistanceValues(min, max);
                    }
                    catch(NumberFormatException nfe) {
                        // Is there anything to do?
                    }
                }
                else {
                    double[] selected;
                    Record r = e.getRecord();
                    double min = 0, max = 0;
                    try {
                        min = Double.parseDouble(r.getAttribute("from"));
                        max = Double.parseDouble(r.getAttribute("to"));
                    }
                    catch(NumberFormatException nfe) {
                        // Is there anything to do?
                    }
                    if (getLocationValues() != null) {
                        double[] val = getLocationValues();
                        selected = new double[val.length + 2];
                        for(int i = 0; i < val.length; i++){
                            selected[i] = val[i];
                        }
                        selected[val.length] = min;
                        selected[val.length + 1] = max;
                    }
                    else {
                        selected = new double[2];
                        selected[0] = min;
                        selected[1] = max;
                    }
                setLocationValues(selected);
                }
            }
        });
        addDistance.setCellIcon(baseUrl + MESSAGES.markerGreen());

        ListGridField ddescr = new ListGridField("description",
                MESSAGES.description());
        ddescr.setType(ListGridFieldType.TEXT);
        ddescr.setWidth("*");
        ListGridField from = new ListGridField("from", MESSAGES.from());
        from.setCellFormatter(cf);

        from.setWidth("12%");
        ListGridField to = new ListGridField("to", MESSAGES.to());
        to.setType(ListGridFieldType.FLOAT);
        to.setCellFormatter(cf);

        to.setWidth("12%");
        to.setAlign(Alignment.LEFT);
        ListGridField dside = new ListGridField("riverside",
                MESSAGES.riverside());
        dside.setType(ListGridFieldType.TEXT);
        dside.setWidth("12%");

        ListGridField bottom =
            new ListGridField("bottom", MESSAGES.bottom_edge());
        bottom.setType(ListGridFieldType.TEXT);
        bottom.setWidth("10%");
        bottom.setCellFormatter(cf);

        ListGridField top =
            new ListGridField("top", MESSAGES.top_edge());
        top.setType(ListGridFieldType.TEXT);
        top.setWidth("10%");
        top.setCellFormatter(cf);

        distanceTable.setFields(
            addDistance, ddescr, from, to, dside, bottom, top);
    }


    /**
     * This method creates a table that contains the location values.
     */
    protected void createLocationTable() {

        String baseUrl = GWT.getHostPageBaseURL();

        locationsTable.setWidth100();
        locationsTable.setShowRecordComponents(true);
        locationsTable.setShowRecordComponentsByCell(true);
        locationsTable.setHeight100();
        locationsTable.setEmptyMessage(MESSAGES.empty_filter());
        locationsTable.setCanReorderFields(false);

        CellFormatter cf = new CellFormatter() {
            @Override
            public String format(
                Object value,
                ListGridRecord record,
                int rowNum, int colNum) {
                    if (value == null) return null;
                    try {
                        NumberFormat nf;
                        double v = Double.parseDouble((String)value);
                        nf = NumberFormat.getFormat("###0.00##");
                        return nf.format(v);
                    }
                    catch (Exception e) {
                        return value.toString();
                    }
                }
            };



        ListGridField addLocation = new ListGridField ("", "");
        addLocation.setType (ListGridFieldType.ICON);
        addLocation.setWidth (20);

        addLocation.addRecordClickHandler (new RecordClickHandler () {
            @Override
            public void onRecordClick (RecordClickEvent e) {
                Record record = e.getRecord();
                double[] selected;
                if (getLocationValues() != null) {
                    double[] val = getLocationValues();
                    selected = new double[val.length + 1];
                    for(int i = 0; i < val.length; i++){
                        selected[i] = val[i];
                    }
                    try {
                        selected[val.length] =
                            Double.parseDouble(record.getAttribute("from"));
                    }
                    catch(NumberFormatException nfe) {
                        // Is there anything to do here?
                    }
                }
                else {
                    selected = new double[1];
                    selected[0] =
                        Double.parseDouble(record.getAttribute("from"));
                }
                setLocationValues(selected);
            }
        });
        addLocation.setCellIcon (baseUrl + MESSAGES.markerGreen ());

        ListGridField ldescr = new ListGridField("description",
                MESSAGES.description());
        ldescr.setType(ListGridFieldType.TEXT);
        ldescr.setWidth("*");
        ListGridField lside = new ListGridField("riverside",
                MESSAGES.riverside());
        lside.setType(ListGridFieldType.TEXT);
        lside.setWidth("12%");
        ListGridField loc = new ListGridField("from", MESSAGES.locations());
        loc.setAlign(Alignment.LEFT);
        loc.setType(ListGridFieldType.FLOAT);
        loc.setWidth("12%");
        loc.setCellFormatter(cf);

        ListGridField bottom =
            new ListGridField("bottom", MESSAGES.bottom_edge());
        bottom.setType(ListGridFieldType.TEXT);
        bottom.setWidth("10%");
        bottom.setCellFormatter(cf);

        ListGridField top =
            new ListGridField("top", MESSAGES.top_edge());
        top.setType(ListGridFieldType.TEXT);
        top.setWidth("10%");
        top.setCellFormatter(cf);

        locationsTable.setFields(addLocation, ldescr, loc, lside, bottom, top);
    }


    /**
     * This method creates a table that contains the location values.
     */
    protected void createLocationTableDistance (){

        String baseUrl = GWT.getHostPageBaseURL();

        locationDistanceTable.setWidth100();
        locationDistanceTable.setShowRecordComponents(true);
        locationDistanceTable.setShowRecordComponentsByCell(true);
        locationDistanceTable.setHeight100();
        locationDistanceTable.setEmptyMessage(MESSAGES.empty_filter());
        locationDistanceTable.setCanReorderFields(false);

        CellFormatter cf = new CellFormatter() {
            @Override
            public String format(
                Object value,
                ListGridRecord record,
                int rowNum, int colNum) {
                    if (value == null) return null;
                    try {
                        NumberFormat nf;
                        double v = Double.parseDouble((String)value);
                        nf = NumberFormat.getFormat("###0.00##");
                        return nf.format(v);
                    }
                    catch (Exception e) {
                        return value.toString();
                    }
            }
        };

        ListGridField addfrom = new ListGridField ("from", MESSAGES.from());
        addfrom.setType (ListGridFieldType.ICON);
        addfrom.setWidth (30);
        addfrom.setCellIcon(baseUrl + MESSAGES.markerGreen());

        ListGridField addto2 = new ListGridField ("to", MESSAGES.to());
        addto2.setType (ListGridFieldType.ICON);
        addto2.setWidth (30);
        addto2.setCellIcon(baseUrl + MESSAGES.markerRed());

        locationDistanceTable.addCellClickHandler (new CellClickHandler () {
            @Override
            public void onCellClick (CellClickEvent e) {
                if (e.getColNum() == 0) {
                    Record r = e.getRecord ();
                    try {
                        double fromvalue =
                             Double.parseDouble(r.getAttribute("from"));
                        double tovalue = getTo ();
                        setDistanceValues (fromvalue, tovalue);
                    }
                    catch(NumberFormatException nfe) {
                        // Is there anything to do in here?
                    }
                }
                else if (e.getColNum() == 1) {
                    Record r = e.getRecord ();
                    try {
                        double fromvalue = getFrom ();
                        double tovalue =
                            Double.parseDouble(r.getAttribute("from"));
                        setDistanceValues (fromvalue, tovalue);
                    }
                    catch(NumberFormatException nfe) {
                        // Is there anything to do in here?
                    }
                }
            }
        });
        ListGridField bottom =
            new ListGridField("bottom", MESSAGES.bottom_edge());
        bottom.setType(ListGridFieldType.TEXT);
        bottom.setWidth("10%");
        bottom.setCellFormatter(cf);

        ListGridField top =
            new ListGridField("top", MESSAGES.top_edge());
        top.setType(ListGridFieldType.TEXT);
        top.setWidth("10%");
        top.setCellFormatter(cf);

        ListGridField ldescr = new ListGridField("description",
                MESSAGES.description());
        ldescr.setType(ListGridFieldType.TEXT);
        ldescr.setWidth("*");
        ListGridField lside = new ListGridField("riverside",
                MESSAGES.riverside());
        lside.setType(ListGridFieldType.TEXT);
        lside.setWidth("12%");
        ListGridField loc = new ListGridField("from", MESSAGES.locations());
        loc.setType(ListGridFieldType.FLOAT);
        loc.setAlign(Alignment.LEFT);
        loc.setWidth("12%");
        loc.setCellFormatter(cf);

        locationDistanceTable.setFields(
            addfrom, addto2, ldescr, loc, lside, bottom, top);
    }


    @Override
    public Canvas createOld(DataList dataList) {
        List<Data> items = dataList.getAll();

        Data       dMode  = getData(items, "ld_mode");
        DataItem[] dItems = dMode.getItems();

        boolean rangeMode = true;
        if (dItems != null && dItems[0] != null) {
            rangeMode = FIELD_VALUE_DISTANCE.equals(dItems[0].getStringValue());
        }

        HLayout layout = new HLayout();
        layout.setWidth("400px");

        Label   label  = new Label(dataList.getLabel());
        label.setWidth("200px");

        Canvas back = getBackButton(dataList.getState());

        layout.addMember(label);

        if (rangeMode) {
            layout.addMember(getOldRangeSelection(dataList));
        }
        else {
            layout.addMember(getOldLocationSelection(dataList));
        }

        layout.addMember(back);

        return layout;
    }


    /**
     * Creates a label for the selected range.
     *
     * @param dataList The DataList containing all values for this state.
     *
     * @return A label displaying the selected values.
     */
    protected Label getOldRangeSelection(DataList dataList) {
        List<Data> items = dataList.getAll();

        Data dFrom = getData(items, "ld_from");
        Data dTo   = getData(items, "ld_to");
        Data dStep = getData(items, "ld_step");

        DataItem[] from = dFrom.getItems();
        DataItem[] to   = dTo.getItems();
        DataItem[] step = dStep.getItems();

        StringBuilder sb = new StringBuilder();
        sb.append(from[0].getLabel());
        sb.append(" " + MESSAGES.unitFrom() + " ");
        sb.append(to[0].getLabel());
        sb.append(" " + MESSAGES.unitTo() + " ");
        sb.append(step[0].getLabel());
        sb.append(" " + MESSAGES.unitWidth());

        Label selected = new Label(sb.toString());
        selected.setWidth("130px");

        return selected;
    }


    /**
     * Creates a label for the selected locations.
     *
     * @param dataList The DataList containing all values for this state.
     *
     * @return A label displaying the selected values.
     */
    protected Label getOldLocationSelection(DataList dataList) {
        List<Data> items = dataList.getAll();

        Data       dLocations = getData(items, "ld_locations");
        DataItem[] lItems     = dLocations.getItems();

        String[] splitted = lItems[0].getStringValue().split(" ");
        String value = "";
        for (int i = 0; i < splitted.length; i++) {
            try {
                NumberFormat nf = NumberFormat.getDecimalFormat();
                double dv = Double.parseDouble(splitted[i]);
                value += nf.format(dv) + " ";
            }
            catch(NumberFormatException nfe) {
                value += splitted[i] + " ";
            }
        }

        Label selected = new Label(value);
        selected.setWidth(130);

        return selected;
    }


    /**
     * This method reads the default values defined in the DataItems of the Data
     * objects in <i>list</i>.
     *
     * @param list The DataList container that stores the Data objects.
     */
    protected void initDefaults(DataList list) {
        Data m = getData(list.getAll(), "ld_mode");
        Data l = getData(list.getAll(), "ld_locations");
        Data f = getData(list.getAll(), "ld_from");
        Data t = getData(list.getAll(), "ld_to");
        Data s = getData(list.getAll(), "ld_step");

        DataItem[] fItems = f.getItems();
        DataItem[] tItems = t.getItems();
        DataItem[] sItems = s.getItems();

        min  = Double.valueOf(fItems[0].getStringValue());
        max  = Double.valueOf(tItems[0].getStringValue());
        step = Double.valueOf(sItems[0].getStringValue());

        DataItem   mDef   = m.getDefault();
        DataItem   lDef   = l.getDefault();
        DataItem   fDef   = f.getDefault();
        DataItem   tDef   = t.getDefault();
        DataItem   sDef   = s.getDefault();

        String mDefValue = mDef != null ? mDef.getStringValue() : null;
        String theMode = mDefValue != null && mDefValue.length() > 0
            ? mDef.getStringValue()
            : FIELD_VALUE_DISTANCE;

        mode.setValue(FIELD_MODE, theMode);

        String fDefValue = fDef != null ? fDef.getStringValue() : null;
        setFrom(fDefValue != null && fDefValue.length() > 0
            ? Double.valueOf(fDef.getStringValue())
            : min);

        String tDefValue = tDef != null ? tDef.getStringValue() : null;
        setTo(tDefValue != null && tDefValue.length() > 0
            ? Double.valueOf(tDef.getStringValue())
            : max);

        String sDefValue = sDef != null ? sDef.getStringValue() : null;
        setStep(sDefValue != null && sDefValue.length() > 0
            ? Double.valueOf(sDef.getStringValue())
            : step);

        if (lDef != null) {
            String lDefValue = lDef != null ? lDef.getStringValue() : null;

            if (lDefValue != null && lDefValue.length() > 0) {
                setLocationValues(lDef.getStringValue());
            }
        }

        if (theMode.equals(FIELD_VALUE_DISTANCE)) {
            enableDistancePanel();
            inputTables.selectTab(1);
        }
        currentFiltered = (ListGrid)inputTables.getSelectedTab().getPane();
    }


    protected Canvas createWidget(DataList data) {
        VLayout layout       = new VLayout();
        container            = new HLayout();
        Canvas checkboxPanel = createRadioButtonPanel();

        // the initial view will display the location input mode
        locationPanel = new DoubleArrayPanel(
                MESSAGES.unitLocation(),
                getLocationValues(),
                this);
        container.addMember(locationPanel);

        layout.addMember(checkboxPanel);
        layout.addMember(container);

        container.setMembersMargin(30);

        inputTables   = new TabSet();
        inputTables.addTabSelectedHandler(new TabSelectedHandler() {
            @Override
            public void onTabSelected(TabSelectedEvent evt) {
                filterDescription.clear();
                filterRange.clear();
                filterResultCount.setValue("");

                Canvas c = evt.getTabPane();
                if(c instanceof ListGrid) {
                    currentFiltered = (ListGrid)c;
                }
            }
        });

        Tab locations = new Tab(MESSAGES.locations());
        Tab distances = new Tab(MESSAGES.distance());

        inputTables.setWidth100();
        inputTables.setHeight100();

        locations.setPane(locationDistanceTable);
        distances.setPane(distanceTable);

        inputTables.addTab(locations);
        inputTables.addTab(distances);

        filterResultCount = new StaticTextItem(MESSAGES.resultCount());
        filterResultCount.setTitleAlign(Alignment.LEFT);
        filterResultCount.setTitleStyle("color: #000");

        filterDescription = new TableFilter();
        filterDescription.setHeight("30px");
        filterDescription.addFilterHandler(this);

        filterRange = new RangeTableFilter();
        filterRange.setHeight("30px");
        filterRange.addFilterHandler(this);
        filterRange.setVisible(false);

        filterCriteria = new SelectItem();
        filterCriteria.setShowTitle(false);
        filterCriteria.setWidth(100);
        filterCriteria.addChangedHandler(new ChangedHandler() {
            @Override
            public void onChanged(ChangedEvent e) {
                if(e.getValue().toString().equals("range")) {
                    filterRange.setVisible(true);
                    filterDescription.setVisible(false);
                    filterDescription.clear();
                    filterResultCount.setValue("");
                }
                else {
                    filterRange.setVisible(false);
                    filterRange.clear();
                    filterDescription.setVisible(true);
                    filterResultCount.setValue("");
                }
            }
        });

        LinkedHashMap<String, String> filterMap =
            new LinkedHashMap<String, String>();
        filterMap.put("description", MESSAGES.description());
        filterMap.put("range", MESSAGES.range());
        filterCriteria.setValueMap(filterMap);
        filterCriteria.setValue("description");

        DynamicForm form = new DynamicForm();
        form.setFields(filterCriteria);
        inputTables.setHeight("*");
        DynamicForm form2 = new DynamicForm();
        form2.setFields(filterResultCount);

        VLayout helper = new VLayout();
        HLayout filterLayout = new HLayout();

        filterLayout.addMember(form);
        filterLayout.addMember(filterDescription);
        filterLayout.addMember(filterRange);
        filterLayout.setHeight("30px");
        helper.addMember(inputTables);
        helper.addMember(filterLayout);
        helper.addMember(form2);
        helper.setHeight100();
        helper.setWidth100();

        helperContainer.addMember(helper);
        filterLayout.setWidth("200");

        return layout;
    }


    @Override
    public void onFilterCriteriaChanged(StringFilterEvent event) {
        String search = event.getFilter();

        if (search != null && search.length() > 0) {
            Criteria c = new Criteria("description", search);

            locationsTable.filterData(c);
            distanceTable.filterData(c);
            locationDistanceTable.filterData(c);
            filterResultCount.setValue(currentFiltered.getRecords().length);
        }
        else {
            locationsTable.clearCriteria();
            distanceTable.clearCriteria();
            locationDistanceTable.clearCriteria();
            filterResultCount.setValue("");
        }
    }


    @Override
    public void onFilterCriteriaChanged(RangeFilterEvent event) {
        Float from = event.getFrom() - 0.001f;
        Float to = event.getTo() + 0.001f;
        GWT.log("filtering range: " + from + " to " + to);


        Criterion combinedFilter = null;
        Criterion locationFilter = null;
        if (from.equals(Float.NaN) && to.equals(Float.NaN)) {
            locationsTable.clearCriteria();
            distanceTable.clearCriteria();
            locationDistanceTable.clearCriteria();
            filterResultCount.setValue("");
            return;
        }
        else if (from.equals(Float.NaN)) {
            combinedFilter = new Criterion("to", OperatorId.LESS_OR_EQUAL, to);
            locationFilter =
                new Criterion("from", OperatorId.LESS_OR_EQUAL, to);
            locationsTable.filterData(locationFilter);
            distanceTable.filterData(combinedFilter);
            locationDistanceTable.filterData(combinedFilter);
            filterResultCount.setValue(currentFiltered.getRecords().length);
            return;
        }
        else if (to.equals(Float.NaN)) {
            combinedFilter =
                new Criterion("from", OperatorId.GREATER_OR_EQUAL, from);
             locationsTable.filterData(combinedFilter);
            distanceTable.filterData(combinedFilter);
            locationDistanceTable.filterData(combinedFilter);
        }
        else {
            AdvancedCriteria c1 =
                new AdvancedCriteria(OperatorId.AND, new Criterion[] {
                    new Criterion("from", OperatorId.GREATER_OR_EQUAL, from),
                    new Criterion("from", OperatorId.LESS_OR_EQUAL, to)
                });

            AdvancedCriteria c2 =
                new AdvancedCriteria(OperatorId.AND, new Criterion[] {
                    new Criterion("to", OperatorId.GREATER_OR_EQUAL, from),
                    new Criterion("to", OperatorId.LESS_OR_EQUAL, to)
                });

            AdvancedCriteria c3 =
                new AdvancedCriteria(OperatorId.AND, new Criterion[] {
                    new Criterion("from", OperatorId.LESS_OR_EQUAL, to),
                    new Criterion("to", OperatorId.GREATER_OR_EQUAL, from)
                });

            combinedFilter =
                new AdvancedCriteria(OperatorId.OR, new Criterion[] {
                    c1, c2, c3
                });
        }
        locationsTable.filterData(combinedFilter);
        distanceTable.filterData(combinedFilter);
        locationDistanceTable.filterData(combinedFilter);
        filterResultCount.setValue(currentFiltered.getRecords().length);
    }


    @Override
    public List<String> validate() {
        if (isLocationMode()) {
            return validateLocations();
        }
        else {
            return validateRange();
        }
    }


    protected List<String> validateLocations() {
        List<String> errors = new ArrayList<String>();
        NumberFormat nf     = NumberFormat.getDecimalFormat();

        try {
            saveLocationValues(locationPanel);
        }
        catch (Exception e) {
            errors.add(MESSAGES.wrongFormat());
        }

        double[] values = getLocationValues();
        double[] good   = new double[values.length];
        int      idx    = 0;

        for (double value: values) {
            if (value < min || value > max) {
                String tmp = MESSAGES.error_validate_range();
                tmp = tmp.replace("$1", nf.format(value));
                tmp = tmp.replace("$2", nf.format(min));
                tmp = tmp.replace("$3", nf.format(max));
                errors.add(tmp);
            }
            else {
                good[idx++] = value;
            }
        }

        double[] justGood = new double[idx];
        for (int i = 0; i < justGood.length; i++) {
            justGood[i] = good[i];
        }

        if (!errors.isEmpty()) {
            locationPanel.setValues(justGood);
        }

        return errors;
    }


    protected List<String> validateRange() {
        List<String> errors = new ArrayList<String>();
        NumberFormat nf     = NumberFormat.getDecimalFormat();

        try {
            saveDistanceValues(distancePanel);
        }
        catch (Exception e) {
            errors.add(MESSAGES.wrongFormat());
        }

        double from = getFrom();
        double to   = getTo();
        double step = getStep();

        if (from < min || from > max) {
            String tmp = MESSAGES.error_validate_range();
            tmp = tmp.replace("$1", nf.format(from));
            tmp = tmp.replace("$2", nf.format(min));
            tmp = tmp.replace("$3", nf.format(max));
            errors.add(tmp);
            from = min;
        }

        if (to < min || to > max) {
            String tmp = MESSAGES.error_validate_range();
            tmp = tmp.replace("$1", nf.format(to));
            tmp = tmp.replace("$2", nf.format(min));
            tmp = tmp.replace("$3", nf.format(max));
            errors.add(tmp);
            to = max;
        }

        if (!errors.isEmpty()) {
            distancePanel.setValues(from, to, step);
        }

        return errors;
    }


    /**
     * This method returns the selected data.
     *
     * @return the selected/inserted data.
     */
    @Override
    public Data[] getData() {
        List<Data> data = new ArrayList<Data>();

        // If we have entered a value and click right afterwards on the
        // 'next' button, the BlurEvent is not fired, and the values are not
        // saved. So, we gonna save those values explicitly.
        if (isLocationMode()) {
            Canvas member = container.getMember(0);
            if (member instanceof DoubleArrayPanel) {
                DoubleArrayPanel form = (DoubleArrayPanel) member;
                saveLocationValues(form);
            }

            Data dLocations = getDataLocations();
            DataItem dFrom  = new DefaultDataItem("ld_from", "ld_from", "");
            DataItem dTo    = new DefaultDataItem("ld_to", "ld_to", "");
            DataItem dStep  = new DefaultDataItem("ld_step", "ld_step", "");

            data.add(dLocations);
            data.add(new DefaultData(
                "ld_from", null, null, new DataItem[] { dFrom } ));
            data.add(new DefaultData(
                "ld_to", null, null, new DataItem[] { dTo } ));
            data.add(new DefaultData(
                "ld_step", null, null, new DataItem[] { dStep } ));
        }
        else {
            Canvas member = container.getMember(0);
            if (member instanceof DoubleRangePanel) {
                DoubleRangePanel form = (DoubleRangePanel) member;
                saveDistanceValues(form);
            }

            Data dFrom   = getDataFrom();
            Data dTo     = getDataTo();
            Data dStep   = getDataStep();
            DataItem loc = new DefaultDataItem("ld_locations", "ld_locations","");

            data.add(dFrom);
            data.add(dTo);
            data.add(dStep);
            data.add(new DefaultData(
                "ld_locations", null, null, new DataItem[] { loc } ));
        }

        Data dMode = getDataMode();
        if (dMode != null) {
            data.add(dMode);
        }

        return data.toArray(new Data[data.size()]);
    }


    /**
     * Returns the Data object for the 'mode' attribute.
     *
     * @return the Data object for the 'mode' attribute.
     */
    protected Data getDataMode() {
        String   value = mode.getValueAsString(FIELD_MODE);
        DataItem item  = new DefaultDataItem("ld_mode", "ld_mode", value);
        return new DefaultData("ld_mode", null, null, new DataItem[] { item });
    }


    protected Data getDataLocations() {
        double[] locations = getLocationValues();
        boolean  first     = true;

        if (locations == null) {
            return null;
        }

        StringBuilder sb = new StringBuilder();

        for (double l: locations) {
            if (!first) {
                sb.append(" ");
            }

            sb.append(l);

            first = false;
        }

        DataItem item = new DefaultDataItem(
            "ld_locations",
            "ld_locations",
            sb.toString());

        return new DefaultData(
            "ld_locations",
            null,
            null,
            new DataItem[] { item });
    }


    /**
     * Returns the Data object for the 'from' attribute.
     *
     * @return the Data object for the 'from' attribute.
     */
    protected Data getDataFrom() {
        String value  = Double.valueOf(getFrom()).toString();
        DataItem item = new DefaultDataItem("ld_from", "ld_from", value);
        return new DefaultData(
            "ld_from", null, null, new DataItem[] { item });
    }


    /**
     * Returns the Data object for the 'to' attribute.
     *
     * @return the Data object for the 'to' attribute.
     */
    protected Data getDataTo() {
        String value  = Double.valueOf(getTo()).toString();
        DataItem item = new DefaultDataItem("ld_to", "ld_to", value);
        return new DefaultData(
            "ld_to", null, null, new DataItem[] { item });
    }


    /**
     * Returns the Data object for the 'step' attribute.
     *
     * @return the Data object for the 'step' attribute.
     */
    protected Data getDataStep() {
        String value  = Double.valueOf(getStep()).toString();
        DataItem item = new DefaultDataItem("ld_step","ld_step", value);
        return new DefaultData(
            "ld_step", null, null, new DataItem[] { item });
    }


    /**
     * Determines the current input mode.
     *
     * @return true, if 'location' is the current input mode, otherwise false.
     */
    public boolean isLocationMode() {
        String inputMode = mode.getValueAsString(FIELD_MODE);

        return inputMode.equals(FIELD_VALUE_LOCATION) ? true : false;
    }


    /**
     * Activates the location panel.
     */
    protected void enableLocationPanel() {
        locationPanel = new DoubleArrayPanel(
            MESSAGES.unitLocation(),
            getLocationValues(),
            this);

        container.removeMembers(container.getMembers());
        container.addMember(locationPanel);
    }


    /**
     * Activates the distance panel.
     */
    protected void enableDistancePanel() {
        distancePanel = new DoubleRangePanel(
            MESSAGES.unitFrom(), MESSAGES.unitTo(), MESSAGES.unitWidth(),
            getFrom(), getTo(), getStep(),
            400,
            this);

        container.removeMembers(container.getMembers());
        container.addMember(distancePanel);
    }


    /**
     * This method switches the input mode between location and distance input.
     *
     * @param event The click event fired by a RadioButtonGroupItem.
     */
    @Override
    public void onChange(ChangeEvent event) {
        String value = (String) event.getValue();

        if (value == null) {
            return;
        }
        if (value.equals(FIELD_VALUE_LOCATION)) {
            enableLocationPanel();
            filterDescription.clear();
            filterRange.clear();
            filterResultCount.setValue("");

            // Remove the tab containing the locationDistanceTable.
            // The 'updateTab()' avoids the tab content to be destroyed.
            inputTables.updateTab(0, null);
            inputTables.removeTab(0);

            // Create a new tab containing the locationTable
            Tab t1 = new Tab (MESSAGES.locations());
            t1.setPane(locationsTable);
            inputTables.addTab(t1, 0);

            // Bring this tab to front.
            inputTables.selectTab(0);
        }
        else {
            enableDistancePanel();
            filterDescription.clear();
            filterRange.clear();
            filterResultCount.setValue("");

            // Remove the tab containing the locationTable.
            // The 'updateTab()' avoids the tab content to be destroyed.
            inputTables.updateTab(0, null);
            inputTables.removeTab(0);

            // Create a new tab containing the locationDistanceTable.
            Tab t1 = new Tab(MESSAGES.locations());
            t1.setPane(locationDistanceTable);
            inputTables.addTab(t1, 0);

            // Bring the distanceTable tab to front.
            inputTables.selectTab(1);
        }
    }


    /**
     * This method is used to validate the inserted data in the form fields.
     *
     * @param event The BlurEvent that gives information about the FormItem that
     * has been modified and its value.
     */
    @Override
    public void onBlur(BlurEvent event) {
        FormItem item = event.getItem();
        String  field = item.getFieldName();

        if (field == null) {
            return;
        }

        if (field.equals(DoubleArrayPanel.FIELD_NAME)) {
            DoubleArrayPanel p = (DoubleArrayPanel) event.getForm();

            saveLocationValue(p, item);
        }
        else {
            DoubleRangePanel p = (DoubleRangePanel) event.getForm();

            saveDistanceValue(p, item);
        }
    }



    /**
     * Validates and stores all values entered in the location mode.
     *
     * @param p The DoubleArrayPanel.
     */
    protected void saveLocationValues(DoubleArrayPanel p) {
        FormItem[] formItems = p.getFields();

        for (FormItem item: formItems) {
            if (item.getFieldName().equals(DoubleArrayPanel.FIELD_NAME)) {
                saveLocationValue(p, item);
            }
        }
    }


    /**
     * Validates and stores all values entered in the distance mode.
     *
     * @param p The DoubleRangePanel.
     */
    protected void saveDistanceValues(DoubleRangePanel p) {
        FormItem[] formItems = p.getFields();

        for (FormItem item: formItems) {
            saveDistanceValue(p, item);
        }
    }


    /**
     * Validates and stores a value entered in the location mode.
     *
     * @param p The DoubleArrayPanel.
     * @param item The item that needs to be validated.
     */
    protected void saveLocationValue(DoubleArrayPanel p, FormItem item) {
        if (p.validateForm(item)) {
            setLocationValues(p.getInputValues(item));
        }
    }


    /**
     * Validates and stores value entered in the distance mode.
     *
     * @param p The DoubleRangePanel.
     * @param item The item that needs to be validated.
     */
    protected void saveDistanceValue(DoubleRangePanel p, FormItem item) {
        if (p.validateForm(item)) {
            setFrom(p.getFrom());
            setTo(p.getTo());
            setStep(p.getStep());
        }
    }


    /**
     * This method creates the panel that contains the checkboxes to switch
     * between the input mode 'location' and 'distance'.
     *
     * @return the checkbox panel.
     */
    protected Canvas createRadioButtonPanel() {
        mode = new DynamicForm();

        RadioGroupItem radio = new RadioGroupItem(FIELD_MODE);
        radio.setShowTitle(false);
        radio.setVertical(false);
        radio.setWrap(false);

        LinkedHashMap<String, String> values = new LinkedHashMap<String, String>();
        values.put(FIELD_VALUE_LOCATION, MESSAGES.location());
        values.put(FIELD_VALUE_DISTANCE, MESSAGES.distance());

        LinkedHashMap<String, String> initial = new LinkedHashMap<String, String>();
        initial.put(FIELD_MODE, FIELD_VALUE_DISTANCE);

        radio.setValueMap(values);
        radio.addChangeHandler(this);

        mode.setFields(radio);
        mode.setValues(initial);

        return mode;
    }


    protected void createDistanceInputPanel() {
        Config config = Config.getInstance();
        String url    = config.getServerUrl();
        String river  = "";

        ArtifactDescription adescr = artifact.getArtifactDescription();
        DataList[] data = adescr.getOldData();

        if (data != null && data.length > 0) {
            for (int i = 0; i < data.length; i++) {
                DataList dl = data[i];
                if (dl.getState().endsWith("river")) {
                    for (int j = 0; j < dl.size(); j++) {
                        Data d = dl.get(j);
                        DataItem[] di = d.getItems();
                        if (di != null && di.length == 1) {
                           river = d.getItems()[0].getStringValue();
                        }
                    }
                }
            }
        }

        distanceTable.setDataSource(new DistanceInfoDataSource(
            url, river, "distances"));
        locationsTable.setDataSource(new DistanceInfoDataSource(
            url, river, "locations"));
        locationDistanceTable.setDataSource(new DistanceInfoDataSource(
            url, river, "locations"));
    }


    protected double getFrom() {
        return from;
    }


    protected void setFrom(double from) {
        this.from = from;
    }


    protected double getTo() {
        return to;
    }


    protected void setTo(double to) {
        this.to = to;
    }


    protected double getStep() {
        return step;
    }


    protected void setStep(double step) {
        this.step = step;
    }


    protected double[] getLocationValues() {
        return values;
    }


    protected void setLocationValues(double[] values) {
        this.values = values;
        locationPanel.setValues(values);
    }


    protected void setLocationValues(String values) {
        String[] vs = values.split(" ");

        if (vs == null) {
            return;
        }

        double[] ds  = new double[vs.length];
        int      idx = 0;

        for (String s: vs) {
            try {
                ds[idx++] = Double.valueOf(s);
            }
            catch (NumberFormatException nfe) {
                // do nothing
            }
        }

        setLocationValues(ds);
    }


    protected void setDistanceValues (double from, double to) {
        setFrom(from);
        setTo(to);
        distancePanel.setValues(from, to, getStep());
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org