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

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

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.Style;
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 Theme theme = record.getTheme();
        final Style style = cia.getStyle(theme.getFacet(), theme.getIndex());
        if (style == null) {
            SC.warn(this.MSG.error_no_theme_styles_found());
            return;
        }

        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