view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/chart/ChartThemePanel.java @ 9416:05405292a7ca

Navigationtheme panel now shows themes of dWt and WQ charts grayed out, if the current station is outside the valid range of the theme.
author gernotbelger
date Thu, 16 Aug 2018 16:28:03 +0200
parents 5e38e2924c07
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