view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/chart/ChartThemePanel.java @ 9580:839b2aa84dd0

minuend/subtrahend-UI-switch
author gernotbelger
date Tue, 08 Jan 2019 17:01:09 +0100
parents 05405292a7ca
children
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.chart;

import org.dive4elements.river.client.client.Config;
import org.dive4elements.river.client.client.services.FeedServiceAsync;
import org.dive4elements.river.client.client.services.LoadArtifactService;
import org.dive4elements.river.client.client.services.LoadArtifactServiceAsync;
import org.dive4elements.river.client.client.ui.CollectionView;
import org.dive4elements.river.client.client.ui.IThemeRecordHandler;
import org.dive4elements.river.client.client.ui.ThemePanel;
import org.dive4elements.river.client.shared.model.Artifact;
import org.dive4elements.river.client.shared.model.Data;
import org.dive4elements.river.client.shared.model.DefaultArtifact;
import org.dive4elements.river.client.shared.model.DefaultData;
import org.dive4elements.river.client.shared.model.FacetRecord;
import org.dive4elements.river.client.shared.model.OutputMode;
import org.dive4elements.river.client.shared.model.Recommendation;
import org.dive4elements.river.client.shared.model.Theme;
import org.dive4elements.river.client.shared.model.ThemeList;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.types.ListGridFieldType;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.menu.Menu;
import com.smartgwt.client.widgets.menu.MenuItem;
import com.smartgwt.client.widgets.menu.events.ClickHandler;
import com.smartgwt.client.widgets.menu.events.MenuItemClickEvent;

/**
 * ThemePanel on the left in CollectionView.
 * Contains control widgets for "themes", which are plotted
 * in a diagram (chart).
 *
 * @author <a href="mailto:ingo.weinzierl@intevation.de">Ingo Weinzierl</a>
 */
public class ChartThemePanel extends ThemePanel {
    /** Artifact Clone/Creation service. */
    private final LoadArtifactServiceAsync loadService = GWT.create(LoadArtifactService.class);

    protected static final String GRID_FIELD_ACTIVE = "active";
    protected static final String GRID_FIELD_NAME = "name";
    protected static final String GRID_FIELD_ACTIONS = "actions";

    protected final FeedServiceAsync feedService = GWT.create(org.dive4elements.river.client.client.services.FeedService.class);

    /** Constructor for a ChartThemePanel.
     * @param recordHandler */
    public ChartThemePanel(final OutputMode mode, final CollectionView view, final IThemeRecordHandler recordHandler) {
        super(mode, view, recordHandler);

        initGrid(recordHandler);
        initLayout();

        updateGrid();
    }

    /** Creates Layout with theme list and navigation bar inside. */
    protected VLayout createLayout() {
        final VLayout layout = new VLayout();
        layout.setWidth100();
        layout.setHeight100();

        layout.addMember(this.list);
        layout.addMember(this.navigation);

        return layout;
    }

    /**
     * Initializes the layout of this panel.
     */
    private void initLayout() {
        setWidth100();
        setHeight100();

        addChild(createLayout());
    }

    /**
     * Initializes the components (columns) of the theme grid.
     * @param recordHandler
     */
    protected void initGrid( final IThemeRecordHandler recordHandler ) {
        this.list.setCanEdit(true);
        this.list.setCanSort(false);
        this.list.setShowRecordComponents(false);
        this.list.setShowRecordComponentsByCell(true);
        this.list.setShowHeader(true);
        this.list.setShowHeaderContextMenu(false);
        this.list.setWidth100();
        this.list.setHeight100();

        this.list.addEditCompleteHandler(this);

        final ListGridField active = new ListGridField(GRID_FIELD_ACTIVE, " ", 20);
        active.setType(ListGridFieldType.BOOLEAN);

        final ListGridField name = new ListGridField(GRID_FIELD_NAME, this.MSG.chart_themepanel_header_themes());
        name.setType(ListGridFieldType.TEXT);

        if( recordHandler instanceof CellFormatter)
            name.setCellFormatter((CellFormatter) recordHandler);

        this.list.setFields(active, name);
    }

    /** Set theme active/inactive. */
    @Override
    public void activateTheme(final Theme theme, final boolean active) {
        theme.setActive(active ? 1 : 0);
    }

    /** Returns name of longitudinal section area facets. */
    protected String getAreaFacetName() {
        return "longitudinal_section.area";
    }

    /** Create the DataProvider ('Blackboard') key for a theme. */
    public static String areaKey(final Theme theme) {
        return theme.getArtifact() + ":" + theme.getFacet() + ":" + theme.getIndex();
    }

    /**
     * Tell an area artifact where to get the upper and lower curve from.
     *
     * @param artifact
     *            UUID of area-artifact.
     */
    public void feedTellArea(final String artifact, final Theme under, final Theme over, final boolean between) {
        Data[] feedData;

        if (over != null && under != null) {
            feedData = new Data[] { DefaultData.createSimpleStringData("area.curve_under", areaKey(under)),
                    DefaultData.createSimpleStringData("area.curve_over", areaKey(over)),
                    DefaultData.createSimpleStringData("area.name", over.getDescription() + " / " + under.getDescription()),
                    DefaultData.createSimpleStringData("area.facet", getAreaFacetName()),
                    DefaultData.createSimpleStringData("area.between", (between) ? "true" : "false") };
            GWT.log("Have 'over' and 'under' curve");
        } else if (over == null && under != null) {
            feedData = new Data[] { DefaultData.createSimpleStringData("area.curve_under", areaKey(under)),
                    DefaultData.createSimpleStringData("area.name", under.getDescription() + " / " + this.MSG.getString("x_axis")),
                    DefaultData.createSimpleStringData("area.facet", getAreaFacetName()),
                    DefaultData.createSimpleStringData("area.between", (between) ? "true" : "false") };
            GWT.log("Have 'under' curve only");
        } else if (over != null && under == null) {
            feedData = new Data[] { DefaultData.createSimpleStringData("area.curve_over", areaKey(over)),
                    DefaultData.createSimpleStringData("area.name", this.MSG.getString("x_axis") + " / " + over.getDescription()),
                    DefaultData.createSimpleStringData("area.facet", getAreaFacetName()),
                    DefaultData.createSimpleStringData("area.between", (between) ? "true" : "false") };
            GWT.log("Have 'over' curve only");
        } else {
            GWT.log("Missing Data for area painting.");
            return;
        }

        this.feedService.feed(Config.getInstance().getLocale(), new DefaultArtifact(artifact, "TODO:hash"), feedData, new AsyncCallback<Artifact>() {
            @Override
            public void onFailure(final Throwable caught) {
                GWT.log("Could not feed artifact (" + artifact + ") with area info: " + caught.getMessage());
                SC.warn(ChartThemePanel.this.MSG.getString(caught.getMessage()));
                enable();
            }

            @Override
            public void onSuccess(final Artifact fartifact) {
                GWT.log("Successfully set area params to " + artifact);
                requestRedraw();
                updateCollection();
                updateGrid();
                enable();
            }
        });
    }

    /**
     * Create and parameterize a new area artifact.
     *
     * @param under
     * @param over
     *            if null, against axis.
     * @param between
     *            if true, ignore under/over order.
     */
    public void createAreaArtifact(final Theme over, final Theme under, final boolean between) {
        final Config config = Config.getInstance();
        final String locale = config.getLocale();

        final Recommendation area = new Recommendation("area", "", "", null);

        // Set target out dynamically.
        area.setTargetOut(getMode().getName());

        final Recommendation[] recommendations = new Recommendation[] { area };

        this.loadService.loadMany(this.getCollection(), recommendations, null, // use individual factories.
                locale, new AsyncCallback<Artifact[]>() {
                    @Override
                    public void onFailure(final Throwable caught) {
                        GWT.log("Failed, no area artifact: " + caught.getMessage());
                        enable();
                        // TODO i18n
                        SC.warn("Failed, no area artifact: " + caught.getMessage());
                    }

                    @Override
                    public void onSuccess(final Artifact[] artifacts) {
                        GWT.log("Success, created area artifact: " + artifacts[0].getUuid());
                        // Now, feed the artifact with the relevant data.
                        feedTellArea(artifacts[0].getUuid(), under, over, between);
                    }
                });
    }

    /**
     * Return true if two themes are canditates for an area being
     * rendered between them.
     * TODO join with canArea, generalize to allow easier modification
     * in subclasses.
     */
    protected boolean areAreaCompatible(final Theme a, final Theme b) {
        if (a.equals(b)) {
            return false;
        }
        if (a.getFacet().equals("w_differences") && b.getFacet().equals("w_differences")) {
            return true;
        }
        if (a.getFacet().equals("longitudinal_section.w") || a.getFacet().equals("other.wqkms.w") || a.getFacet().equals("other.wqkms")
                || a.getFacet().equals("discharge_longitudinal_section.w") || a.getFacet().equals("discharge_longitudinal_section.c")
                || a.getFacet().equals("other.wkms")) {
            return b.getFacet().equals("longitudinal_section.w") || b.getFacet().equals("other.wqkms") || b.getFacet().equals("other.wqkms.w")
                    || b.getFacet().equals("discharge_longitudinal_section.w") || b.getFacet().equals("discharge_longitudinal_section.c")
                    || b.getFacet().equals("other.wkms");
        } else if (a.getFacet().equals("longitudinal_section.q") || a.getFacet().equals("discharge_longitudinal_section.q")
                || a.getFacet().equals("other.wqkms.q")) {
            return b.getFacet().equals("longitudinal_section.q") || b.getFacet().equals("discharge_longitudinal_section.q")
                    || b.getFacet().equals("other.wqkms.q");
        }
        return false;
    }

    /**
     * True if context menu should contain 'create area' submenu on
     * this theme.
     */
    protected boolean canArea(final Theme a) {
        return a.getFacet().equals("longitudinal_section.q") || a.getFacet().equals("longitudinal_section.w")
                || a.getFacet().equals("discharge_longitudinal_section.w") || a.getFacet().equals("discharge_longitudinal_section.q")
                || a.getFacet().equals("discharge_longitudinal_section.c") || a.getFacet().startsWith("other.wqkms") || a.getFacet().equals("other.wkms")
                || a.getFacet().equals("w_differences");
    }

    /** Attach menu/item to open editor for Manual Points. */
    protected void attachManualPointsMenu(final Menu menu) {
        menu.addItem(createSeparator());
        final MenuItem editManualPoints = new MenuItem(this.MSG.editpoints());

        editManualPoints.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final MenuItemClickEvent evt) {
                if (ChartThemePanel.this.mode.getName().equals("historical_discharge")) {
                    new ManualDatePointsEditor(ChartThemePanel.this.view.getCollection(), ChartThemePanel.this.redrawRequestHandlers.get(0),
                            ChartThemePanel.this.mode.getName()).show();
                } else {
                    new ManualPointsEditor(ChartThemePanel.this.view.getCollection(), ChartThemePanel.this.redrawRequestHandlers.get(0),
                            ChartThemePanel.this.mode.getName()).show();
                }
            }
        });
        menu.addItem(editManualPoints);
    }

    /**
     * Include area specific menu items and manual point editor, depending
     * on facet.
     */
    @Override
    protected Menu getSingleContextMenu(final ListGridRecord[] records) {
        final Menu menu = super.getSingleContextMenu(records);

        final Theme facetTheme = ((FacetRecord) records[0]).getTheme();

        if (!canArea(facetTheme)) {
            if (facetTheme.getFacet().endsWith("manualpoints")) {
                attachManualPointsMenu(menu);
                return menu;
            } else {
                return menu;
            }
        }

        menu.addItem(createSeparator());

        final MenuItem areaMenuItem = new MenuItem(this.MSG.chart_themepanel_new_area());
        final Menu areaMenu = new Menu();

        final ThemeList themes = getThemeList();
        final int nThemes = themes.getThemeCount();

        // Create the "under..." submenu.
        final MenuItem underMenuItem = new MenuItem(this.MSG.chart_themepanel_area_under());
        final Menu underMenu = new Menu();
        for (int i = 0; i < nThemes; i++) {
            final Theme theme = themes.getThemeAt(i + 1);

            if (theme.getVisible() == 0) {
                continue;
            }

            if (!areAreaCompatible(facetTheme, theme)) {
                continue;
            }

            final MenuItem againster = new MenuItem(theme.getDescription());
            underMenu.addItem(againster);

            againster.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(final MenuItemClickEvent evt) {
                    disable();
                    createAreaArtifact(theme, facetTheme, false);
                }
            });
        }

        // Create the "over..." submenu.
        final MenuItem overMenuItem = new MenuItem(this.MSG.chart_themepanel_area_over());
        final Menu overMenu = new Menu();
        for (int i = 0; i < nThemes; i++) {
            final Theme theme = themes.getThemeAt(i + 1);
            if (theme.getVisible() == 0) {
                continue;
            }
            if (!areAreaCompatible(facetTheme, theme)) {
                continue;
            }
            final MenuItem againster = new MenuItem(theme.getDescription());
            overMenu.addItem(againster);

            againster.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(final MenuItemClickEvent evt) {
                    disable();
                    createAreaArtifact(facetTheme, theme, false);
                }
            });
        }
        overMenu.addItem(createSeparator());
        final MenuItem againstAxis = new MenuItem(this.MSG.getString("x_axis"));
        againstAxis.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final MenuItemClickEvent evt) {
                disable();
                createAreaArtifact(null, facetTheme, false);
            }
        });
        overMenu.addItem(againstAxis);

        // Create the "between..." submenu.
        final MenuItem betweenMenuItem = new MenuItem(this.MSG.chart_themepanel_area_between());
        final Menu betweenMenu = new Menu();
        for (int i = 0; i < nThemes; i++) {
            final Theme theme = themes.getThemeAt(i + 1);
            if (theme.getVisible() == 0) {
                continue;
            }
            if (!areAreaCompatible(facetTheme, theme)) {
                continue;
            }
            final MenuItem againster = new MenuItem(theme.getDescription());
            betweenMenu.addItem(againster);

            againster.addClickHandler(new ClickHandler() {
                @Override
                public void onClick(final MenuItemClickEvent evt) {
                    disable();
                    createAreaArtifact(facetTheme, theme, true);
                }
            });
        }
        betweenMenu.addItem(createSeparator());
        betweenMenu.addItem(againstAxis);

        overMenuItem.setSubmenu(overMenu);
        underMenuItem.setSubmenu(underMenu);
        betweenMenuItem.setSubmenu(betweenMenu);

        areaMenu.addItem(betweenMenuItem);
        areaMenu.addItem(overMenuItem);
        areaMenu.addItem(underMenuItem);
        areaMenu.addItem(createSeparator());
        final MenuItem standAloneAgainstAxis = new MenuItem(this.MSG.getString("against_x_axis"));
        standAloneAgainstAxis.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final MenuItemClickEvent evt) {
                disable();
                createAreaArtifact(null, facetTheme, false);
            }
        });
        areaMenu.addItem(standAloneAgainstAxis);

        areaMenuItem.setSubmenu(areaMenu);
        menu.addItem(areaMenuItem);

        return menu;
    }
}

http://dive4elements.wald.intevation.org