view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/ThemePanel.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 cff7636035ce
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 java.util.ArrayList;
import java.util.List;

import org.dive4elements.river.client.client.Config;
import org.dive4elements.river.client.client.FLYSConstants;
import org.dive4elements.river.client.client.event.HasOutputParameterChangeHandlers;
import org.dive4elements.river.client.client.event.HasRedrawRequestHandlers;
import org.dive4elements.river.client.client.event.OnMoveEvent;
import org.dive4elements.river.client.client.event.OnMoveHandler;
import org.dive4elements.river.client.client.event.OutputParameterChangeEvent;
import org.dive4elements.river.client.client.event.OutputParameterChangeHandler;
import org.dive4elements.river.client.client.event.RedrawRequestEvent;
import org.dive4elements.river.client.client.event.RedrawRequestEvent.Type;
import org.dive4elements.river.client.client.event.RedrawRequestHandler;
import org.dive4elements.river.client.client.services.CollectionAttributeService;
import org.dive4elements.river.client.client.services.CollectionAttributeServiceAsync;
import org.dive4elements.river.client.client.services.CollectionItemAttributeService;
import org.dive4elements.river.client.client.services.CollectionItemAttributeServiceAsync;
import org.dive4elements.river.client.shared.model.Collection;
import org.dive4elements.river.client.shared.model.CollectionItemAttribute;
import org.dive4elements.river.client.shared.model.FacetRecord;
import org.dive4elements.river.client.shared.model.OutputMode;
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.util.BooleanCallback;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.EditCompleteEvent;
import com.smartgwt.client.widgets.grid.events.EditCompleteHandler;
import com.smartgwt.client.widgets.grid.events.RowContextClickEvent;
import com.smartgwt.client.widgets.grid.events.RowContextClickHandler;
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 abstract class ThemePanel extends Canvas implements OnMoveHandler, EditCompleteHandler, HasOutputParameterChangeHandlers, HasRedrawRequestHandlers {

    private final CollectionAttributeServiceAsync updater = GWT.create(CollectionAttributeService.class);

    /** The service used to get collection item attributes. */
    private final CollectionItemAttributeServiceAsync itemAttributeService = GWT.create(CollectionItemAttributeService.class);

    /** i18ner. */
    protected final FLYSConstants MSG = GWT.create(FLYSConstants.class);

    /** List of OutParameterChangedHandler. */
    private final List<OutputParameterChangeHandler> outHandlers;

    /** List of ChartShallRedrawHandler. */
    protected final List<RedrawRequestHandler> redrawRequestHandlers;

    protected final OutputMode mode;

    protected final ThemeNavigationPanel navigation;

    protected final ListGrid list;

    /** The collection view */
    protected CollectionView view;

    private final IThemeRecordHandler recordHandler;

    /**
     * Setup Grid, navigation bar.
     *
     * @param collection
     *            Collection for which to show themes.
     */
    public ThemePanel(final OutputMode mode, final CollectionView view, final IThemeRecordHandler recordHandler) {
        this.mode = mode;
        this.recordHandler = recordHandler;
        this.list = createGrid();
        this.view = view;

        this.list.addRowContextClickHandler(new RowContextClickHandler() {
            @Override
            public void onRowContextClick(final RowContextClickEvent event) {
                final ListGridRecord[] records = ThemePanel.this.list.getSelectedRecords();

                Menu menu = null;

                if (records == null || records.length == 0) {
                    return;
                } else if (records.length == 1) {
                    menu = getSingleContextMenu(records);
                } else if (records.length > 1) {
                    menu = getMultiContextMenu(records);
                }

                if (menu != null) {
                    ThemePanel.this.list.setContextMenu(menu);
                    menu.showContextMenu();

                    event.cancel();
                }
            }
        });

        this.redrawRequestHandlers = new ArrayList<RedrawRequestHandler>();
        this.outHandlers = new ArrayList<OutputParameterChangeHandler>();
        this.navigation = new ThemeNavigationPanel();
        this.navigation.addOnMoveHandler(this);

        this.setShowResizeBar(true);
    }

    public abstract void activateTheme(Theme theme, boolean active);

    /**
     * Replace the current collection with a new one. <b>NOTE: this operation
     * triggers updateGrid() which modifies the themes in the grid.</b>
     *
     * @param collection
     *            The new collection object.
     */
    protected void setCollection(final Collection collection) {
        // Set collection of view, but do not trigger event shooting.
        this.view.setCollection(collection, true);

        updateGrid();
    }

    /** Get Collection. */
    public Collection getCollection() {
        return this.view.getCollection();
    }

    /**
     * Returns the ThemeList of the current collection and output mode.
     *
     * @return the current ThemeList.
     */
    public ThemeList getThemeList() {
        return getCollection().getThemeList(this.mode.getName());
    }

    public ListGridRecord[] getSelectedRecords() {
        return this.list.getSelectedRecords();
    }

    /**
     * Registers a new OutputParameterChangeHandler.
     *
     * @param h
     *            The new handler.
     */
    @Override
    public void addOutputParameterChangeHandler(final OutputParameterChangeHandler h) {
        if (h != null) {
            this.outHandlers.add(h);
        }
    }

    /**
     * Registers a RedrawRequestHandler.
     *
     * @param h
     *            The new handler.
     */
    @Override
    public void addRedrawRequestHandler(final RedrawRequestHandler h) {
        if (h != null) {
            this.redrawRequestHandlers.add(h);
        }
    }

    /**
     * Request a redraw of e.g. a Chart.
     */
    final public void requestRedraw() {
        for (final RedrawRequestHandler handler : this.redrawRequestHandlers) {
            handler.onRedrawRequest(new RedrawRequestEvent(Type.DEFAULT));
        }
    }

    /**
     * Called when the attribution of an output changed. It informs the
     * registered handlers about the changes.
     */
    protected void fireOutputParameterChanged() {
        final OutputParameterChangeEvent evt = new OutputParameterChangeEvent();

        for (final OutputParameterChangeHandler handler : this.outHandlers) {
            handler.onOutputParameterChanged(evt);
        }
    }

    /** Registers the CollectionView associated to this ThemePanel. */
    public void setCollectionView(final CollectionView view) {
        this.view = view;
    }

    /**
     * This method is used to clear the current theme grid and add new updated
     * data.
     */
    protected void updateGrid() {
        GWT.log("ThemePanel.updateGrid");

        final ListGridRecord[] selected = this.list.getSelectedRecords();

        clearGrid();

        final ThemeList themeList = getThemeList();

        if (themeList == null) {
            GWT.log("ERROR: No theme list.");
            return;
        }

        final int count = themeList.getThemeCount();

        for (int i = 1; i <= count; i++) {
            final Theme theme = themeList.getThemeAt(i);

            if (theme == null) {
                continue;
            }

            if (theme.getFacet().equals("empty.facet")) {
                theme.setVisible(0);
            }

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

            final FacetRecord newRecord = createRecord(theme);
            addFacetRecord(newRecord);

            final String newArtifact = theme.getArtifact();
            final String newFacet = theme.getFacet();
            final int newIndex = theme.getIndex();

            for (final ListGridRecord r : selected) {
                final FacetRecord sel = (FacetRecord) r;
                final Theme oldTheme = sel.getTheme();

                if (oldTheme.getArtifact().equals(newArtifact) && oldTheme.getFacet().equals(newFacet) && oldTheme.getIndex() == newIndex) {
                    this.list.selectRecord(newRecord);
                }
            }
        }

        updateThemes();

        fireOutputParameterChanged();
    }

    /** Adds given Record to the list (table). */
    protected void addFacetRecord(final FacetRecord rec) {
        this.list.addData(rec);
    }

    /** Create a FacetRecord that wraps given theme. */
    protected final FacetRecord createRecord(final Theme theme) {
        return new FacetRecord(theme);
    }

    /**
     * This method triggers the CollectionAttributeService. Based on the current
     * collectin settings, the attribute of the collection is modified or not.
     * But in every case, we will get a new collection object - which might be
     * the same as the current one.
     */
    public void updateCollection() {
        final Config config = Config.getInstance();
        final String loc = config.getLocale();

        GWT.log("ThemePanel.updateCollection via RPC now");

        // Don't forget to enable the panel after the request has finished!
        disable();

        this.updater.update(getCollection(), loc, new AsyncCallback<Collection>() {
            @Override
            public void onFailure(final Throwable caught) {
                GWT.log("Could not update collection attributes.");
                SC.warn(ThemePanel.this.MSG.getString(caught.getMessage()));

                enable();
            }

            @Override
            public void onSuccess(final Collection collection) {
                setCollection(collection);

                enable();
            }
        });
    }

    /**
     * Create and configure the Grid to display.
     */
    protected ListGrid createGrid() {
        final ListGrid grid = new ListGrid();
        grid.setLeaveScrollbarGap(false);

        return grid;
    }

    /**
     * A method that removes all records from theme grid.
     */
    protected void clearGrid() {
        final ListGridRecord[] records = this.list.getRecords();

        if (records == null || records.length == 0) {
            return;
        }

        for (final ListGridRecord record : records) {
            this.list.removeData(record);
        }
    }

    /** Return 'separator'- menu-item. */
    protected MenuItem createSeparator() {
        final MenuItem separator = new MenuItem();
        separator.setIsSeparator(true);
        return separator;
    }

    /**
     * Get the context menu for a (right mouse button)click on a single item.
     */
    protected Menu getSingleContextMenu(final ListGridRecord[] records) {
        final Menu menu = new Menu();

        menu.addItem(createActivateItem(records));
        menu.addItem(createDeactivateItem(records));
        menu.addItem(createRemoveItem(records));
        menu.addItem(createSeparator());
        menu.addItem(createPropertiesItem(records));

        return menu;
    }

    protected Menu getMultiContextMenu(final ListGridRecord[] records) {
        final Menu menu = new Menu();

        menu.addItem(createActivateItem(records));
        menu.addItem(createDeactivateItem(records));
        menu.addItem(createRemoveItem(records));

        return menu;
    }

    /** The properties menu item (opens style editor on click). */
    protected MenuItem createPropertiesItem(final ListGridRecord[] records) {
        final MenuItem properties = new MenuItem(this.MSG.properties());

        properties.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final MenuItemClickEvent evt) {
                GWT.log("clicked properties");
                for (final ListGridRecord record : records) {
                    openStyleEditor((FacetRecord) record);
                }
            }
        });

        return properties;
    }

    protected MenuItem createActivateItem(final ListGridRecord[] records) {
        final MenuItem activate = new MenuItem(this.MSG.activateTheme());

        activate.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final MenuItemClickEvent evt) {
                for (final ListGridRecord record : records) {
                    final FacetRecord facet = (FacetRecord) record;
                    activateTheme(facet.getTheme(), true);
                }

                updateCollection();
            }
        });

        return activate;
    }

    protected MenuItem createDeactivateItem(final ListGridRecord[] records) {
        final MenuItem deactivate = new MenuItem(this.MSG.deactivateTheme());

        deactivate.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final MenuItemClickEvent evt) {
                for (final ListGridRecord record : records) {
                    final FacetRecord facet = (FacetRecord) record;
                    activateTheme(facet.getTheme(), false);
                }

                updateCollection();
            }
        });

        return deactivate;
    }

    /** Remove given themes (not asking for confirmation). */
    protected void removeThemes(final ListGridRecord[] records) {
        for (final ListGridRecord record : records) {
            final FacetRecord facet = (FacetRecord) record;
            final Theme theme = facet.getTheme();
            theme.setVisible(0);
            theme.setActive(0);
            updateCollection();
        }
    }

    /** Create menu item for removing theme(s). Will ask for confirmation. */
    protected MenuItem createRemoveItem(final ListGridRecord[] records) {
        final MenuItem remove = new MenuItem(this.MSG.removeTheme());

        remove.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(final MenuItemClickEvent evt) {
                SC.ask(ThemePanel.this.MSG.askThemeRemove(), new BooleanCallback() {
                    @Override
                    public void execute(final Boolean value) {
                        if (value) {
                            removeThemes(records);
                        }
                    }
                });
            }
        });

        return remove;
    }

    /**
     * This method is called after a cell in the theme grid has been modified.
     *
     * @param event
     *            The event that stores information about the modified record.
     */
    @Override
    public void onEditComplete(final EditCompleteEvent event) {
        GWT.log("Edited record.");

        final int row = event.getRowNum();
        final FacetRecord rec = (FacetRecord) this.list.getRecord(row);

        final Theme theme = rec.getTheme();

        theme.setDescription(rec.getName());
        activateTheme(theme, rec.getActive());

        updateCollection();
    }

    /**
     * This method should be defined in subclasses that wants to listen to this
     * event.
     *
     * @param theme
     *            The theme that is moved.
     * @param oldIdx
     *            The index of the theme before it was moved.
     * @param newIdx
     *            The index of the theme after it was moved.
     */
    protected void fireThemeMoved(final Theme theme, final int oldIdx, final int newIdx) {
        // Do nothing
    }

    @Override
    public void onMove(final OnMoveEvent event) {
        final int type = event.getType();

        GWT.log("ThemePanel.onMove: " + type);

        final ListGridRecord[] records = this.list.getSelectedRecords();

        if (records == null || records.length == 0) {
            GWT.log("ThemePanel.onMove: No records selected.");
            return;
        }

        switch (type) {
        case 0:
            moveRecordsTop(records);
            break;
        case 1:
            moveRecordsUp(records);
            break;
        case 2:
            moveRecordsDown(records);
            break;
        case 3:
            moveRecordsBottom(records);
            break;
        }

        updateCollection();
    }

    /**
     * Moves the selected grid records (themes) to the top of the grid.
     *
     * @param records
     *            The selected themes in the list. Null not permitted.
     */
    protected void moveRecordsTop(final ListGridRecord[] records) {
        final ThemeList themeList = getThemeList();

        int idx = 1;

        for (final ListGridRecord record : records) {
            final Theme theme = ((FacetRecord) record).getTheme();
            fireThemeMoved(theme, theme.getPosition(), idx);
            themeList.setThemePosition(theme, idx++);
        }

        updateGrid();
    }

    /**
     * Moves the selected grid records (themes) one step up.
     *
     * @param records
     *            The selected themes in the list. Null not permitted.
     */
    protected void moveRecordsUp(final ListGridRecord[] records) {
        final ThemeList themeList = getThemeList();

        final int[] newPos = new int[records.length];

        for (int i = 0; i < records.length; i++) {
            final Theme theme = ((FacetRecord) records[i]).getTheme();
            newPos[i] = theme.getPosition() - 1;
        }

        for (int i = 0; i < records.length; i++) {
            final Theme theme = ((FacetRecord) records[i]).getTheme();
            fireThemeMoved(theme, theme.getPosition(), newPos[i]);
            themeList.setThemePosition(theme, newPos[i]);
        }

        updateGrid();
    }

    /**
     * Moves the selected grid records (themes) one step down.
     *
     * @param records
     *            The selected themes in the list. Null not permitted.
     */
    protected void moveRecordsDown(final ListGridRecord[] records) {
        final ThemeList themeList = getThemeList();

        final int[] newPos = new int[records.length];

        for (int i = records.length - 1; i >= 0; i--) {
            final Theme theme = ((FacetRecord) records[i]).getTheme();
            newPos[i] = theme.getPosition() + 1;
        }

        for (int i = records.length - 1; i >= 0; i--) {
            final Theme theme = ((FacetRecord) records[i]).getTheme();
            fireThemeMoved(theme, theme.getPosition(), newPos[i]);
            themeList.setThemePosition(theme, newPos[i]);
        }

        updateGrid();
    }

    /**
     * Moves the selected grid records (themes) to the bottom of the grid.
     *
     * @param records
     *            The selected themes in the list. Null not permitted.
     */
    protected void moveRecordsBottom(final ListGridRecord[] records) {
        final ThemeList themeList = getThemeList();

        int idx = themeList.getThemeCount();

        for (int i = records.length - 1; i >= 0; i--) {
            final Theme theme = ((FacetRecord) records[i]).getTheme();
            fireThemeMoved(theme, theme.getPosition(), idx);
            themeList.setThemePosition(theme, idx--);
        }

        updateGrid();
    }

    protected void openStyleEditor(final FacetRecord record) {
        final Config config = Config.getInstance();
        final String locale = config.getLocale();

        final String artifact = record.getTheme().getArtifact();

        this.itemAttributeService.getCollectionItemAttribute(this.getCollection(), artifact, locale, new AsyncCallback<CollectionItemAttribute>() {
            @Override
            public void onFailure(final Throwable caught) {
                SC.warn(ThemePanel.this.MSG.getString(caught.getMessage()));
            }

            @Override
            public void onSuccess(final CollectionItemAttribute cia) {
                GWT.log("Successfully loaded collectionitem attributes.");
                showStyleEditor(cia, record);
            }
        });
    }

    protected void showStyleEditor(final CollectionItemAttribute cia, final FacetRecord record) {
        final StyleEditorWindow win = new StyleEditorWindow(getCollection(), cia, record);
        win.setThemePanel(this);
        win.centerInPage();
        win.show();
    }

    /** Get OutputMode of this Panel. */
    public OutputMode getMode() {
        return this.mode;
    }

    public void updateThemes() {

        final ListGridRecord[] records = this.list.getRecords();
        for (final ListGridRecord record : records) {
            final FacetRecord facetRecord = (FacetRecord) record;

            this.recordHandler.handle(facetRecord);
        }

        this.list.markForRedraw();
    }
}

http://dive4elements.wald.intevation.org