view flys-client/src/main/java/de/intevation/flys/client/client/ui/DatacageWidget.java @ 5462:f2371f3aaf03

Show top level folder icons only if node has no factory If you have an empty folder the folder icon is still shown. This makes it possible to add functional "Top Level" entries in the Datacage
author Andre Heinecke <aheinecke@intevation.de>
date Tue, 26 Mar 2013 18:29:13 +0100
parents 7558a18ceb23
children 6ec7c8ebb2c9
line wrap: on
line source
package de.intevation.flys.client.client.ui;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;

import com.smartgwt.client.types.SelectionStyle;
import com.smartgwt.client.types.TreeModelType;
import com.smartgwt.client.util.SC;
import com.smartgwt.client.widgets.Button;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordDoubleClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordDoubleClickHandler;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.tree.Tree;
import com.smartgwt.client.widgets.tree.TreeGrid;
import com.smartgwt.client.widgets.tree.TreeNode;

import de.intevation.flys.client.client.Config;
import de.intevation.flys.client.client.FLYSConstants;
import de.intevation.flys.client.client.event.DatacageDoubleClickHandler;
import de.intevation.flys.client.client.event.DatacageHandler;
import de.intevation.flys.client.client.services.MetaDataService;
import de.intevation.flys.client.client.services.MetaDataServiceAsync;
import de.intevation.flys.client.shared.model.Artifact;
import de.intevation.flys.client.shared.model.AttrList;
import de.intevation.flys.client.shared.model.DataCageNode;
import de.intevation.flys.client.shared.model.DataCageTree;
import de.intevation.flys.client.shared.model.ToLoad;
import de.intevation.flys.client.shared.model.User;

import java.util.ArrayList;
import java.util.List;
import java.util.MissingResourceException;
import java.util.Stack;

// TODO: refactor, extract ~DataCageGrid
/**
 * Display tree of, for example, previous calculations and allows
 * selection in order to access/clone these.
 */
public class DatacageWidget
extends      VLayout
{
    public static final int MAX_OPEN = 30;

    protected MetaDataServiceAsync metaDataService =
        GWT.create(MetaDataService.class);

    protected FLYSConstants messages =
        GWT.create(FLYSConstants.class);

    protected Artifact artifact;
    protected User     user;
    protected String   outs;
    protected String   parameters;

    protected TreeGrid treeGrid;
    protected Tree     tree;

    protected ToLoad   toLoad;

    protected List<DatacageHandler>            handlers;
    protected List<DatacageDoubleClickHandler> doubleHandlers;


    public DatacageWidget() {
        handlers       = new ArrayList<DatacageHandler>();
        doubleHandlers = new ArrayList<DatacageDoubleClickHandler>();
    }


    public DatacageWidget(Artifact artifact, User user) {
        this(artifact, user, null);
    }

    public DatacageWidget(Artifact artifact, User user, String outs) {
        this(artifact, user, outs, true);
    }

    public DatacageWidget(
        Artifact   artifact,
        User       user,
        String     outs,
        boolean    showButton
    ) {
        this(artifact, user, outs, null, showButton);
    }


    public DatacageWidget(
        Artifact   artifact,
        User       user,
        String     outs,
        String     parameters,
        boolean    showButton
    ) {
        this();

        this.artifact   = artifact;
        this.user       = user;
        this.outs       = outs;
        this.parameters = parameters;

        toLoad = new ToLoad();

        setWidth100();

        tree = new Tree();
        tree.setModelType(TreeModelType.CHILDREN);
        tree.setNameProperty("name");
        tree.setIdField("id");
        tree.setChildrenProperty("children-nodes");
        tree.setShowRoot(false);

        treeGrid = new TreeGrid();
        treeGrid.setLoadDataOnDemand(false);
        treeGrid.setWidth100();
        treeGrid.setHeight100();
        treeGrid.setShowRoot(false);
        treeGrid.setNodeIcon("[SKIN]/../blank.gif");
        treeGrid.setShowConnectors(true);
        treeGrid.setLoadingMessage(messages.databasket_loading());
        treeGrid.setEmptyMessage(messages.databasket_loading());
        treeGrid.setLoadingDataMessage(messages.databasket_loading());

        treeGrid.setHoverMoveWithMouse(true);
        treeGrid.setCanHover(true);
        treeGrid.setShowHover(true);
        treeGrid.setHoverOpacity(75);

        treeGrid.setHoverCustomizer(new HoverCustomizer() {
            @Override
            public String hoverHTML(Object value,
                ListGridRecord record,
                int rowNum,
                int colNum
                ) {
                if(record instanceof TreeNode) {
                    TreeNode hoveredTreeNode = (TreeNode)record;
                    String info = hoveredTreeNode.getAttribute("info");
                    if (info == null) {
                        info = hoveredTreeNode.getName();
                    }
                    return info;
                }
                else {
                    return "";// should not happen
                }
            }
        });

        treeGrid.addRecordDoubleClickHandler(new RecordDoubleClickHandler() {
            @Override
            public void onRecordDoubleClick(RecordDoubleClickEvent event) {
                doubleClickedOnTree(event);
            }
        });

        addMember(treeGrid);

        if (showButton) {
            addMember(createPlusButton());
        }

        triggerTreeBuilding();
    }


    /**
     * @param handler Handler to be added (notified on add-action).
     */
    public DatacageWidget(Artifact artifact, User user, String outs,
        DatacageHandler handler) {
        this(artifact, user, outs);
        this.addDatacageHandler(handler);
    }


    public DatacageWidget(
        Artifact        artifact,
        User            user,
        String          outs,
        DatacageHandler handler,
        String          parameters
    ) {
        this(artifact, user, outs, handler);
        this.parameters = parameters;
    }


    /**
     * Sets whether more than one item can be selected.
     * @param multi if true, allow mutliple selections.
     */
    public void setIsMutliSelectable(boolean multi) {
        if (multi) {
            treeGrid.setSelectionType(SelectionStyle.MULTIPLE);
        }
        else {
            treeGrid.setSelectionType(SelectionStyle.SINGLE);
        }
    }


    /**
     * @param handler Handler to be added (notified on add-action).
     */
    public void addDatacageHandler(DatacageHandler handler) {
        if (!handlers.contains(handler)) {
            handlers.add(handler);
        }
    }


    /**
     * @param h Handler to be added (notified on Double click on node).
     */
    public void addDatacageDoubleClickHandler(DatacageDoubleClickHandler h) {
        if (!doubleHandlers.contains(h)) {
            doubleHandlers.add(h);
        }
    }


    /**
     * @param handler Handler to remove from list.
     */
    public void removeDatacageHandler(DatacageHandler handler) {
        handlers.remove(handler);
    }


    public ToLoad getToLoad() {
        return toLoad;
    }


    public ToLoad getSelection() {
        // Reset content of toLoads.
        toLoad = new ToLoad();

        if (treeGrid == null) {
            return toLoad;
        }

        ListGridRecord [] selection = treeGrid.getSelectedRecords();

        if (selection != null) {
            for (ListGridRecord record: selection) {
                if (record instanceof TreeNode) {
                    collectToLoads((TreeNode)record);
                }
            }
        }

        return toLoad;
    }


    /**
     * Returns the titles of selected items (if any).
     */
    public String[] getSelectionTitles() {
        if (treeGrid == null) {
            return new String[] {};
        }

        ListGridRecord [] selection = treeGrid.getSelectedRecords();

        if (selection == null) {
            return new String[] {};
        }

        List<String> titleList = new ArrayList<String>();
        for (ListGridRecord record: selection) {
            if (record instanceof TreeNode) {
                titleList.add(((TreeNode)record).getAttribute("name"));
            }
        }

        return titleList.toArray(new String[titleList.size()]);
    }


    /**
     * Callback for add-button.
     * Fires to load for every selected element and handler.
     */
    public void plusClicked() {
        if (!getSelection().isEmpty()) {
            fireToLoad();
        }
    }


    protected Button createPlusButton() {
        Button plusBtn = new Button(messages.datacageAdd());
        plusBtn.addClickHandler(new ClickHandler() {
            @Override
            public void onClick(ClickEvent event) {
                plusClicked();
            }
        });
        return plusBtn;
    }


    protected void fireToLoad() {
        for (DatacageHandler handler: handlers) {
            handler.toLoad(toLoad);
        }
    }


    /** Notify DatacageDoubleClickHandlers that a doubleclick happened. */
    protected void fireOnDoubleClick() {
        for (DatacageDoubleClickHandler handler: doubleHandlers) {
            handler.onDoubleClick(toLoad);
        }
    }


    protected void doubleClickedOnTree(RecordDoubleClickEvent event) {
        TreeNode node = (TreeNode)event.getRecord();
        collectToLoads(node);
        fireOnDoubleClick();
    }


    /**
     * Adds to toLoad, from info in node.
     * Afterwards, add all children of node to stack to parse (next time
     * collectToLoads is called).
     */
    protected void collectToLoads(TreeNode node) {
        Stack<TreeNode> stack = new Stack<TreeNode>();

        stack.push(node);

        while (!stack.isEmpty()) {
            node = stack.pop();
            String factory = node.getAttribute("factory");
            if (factory != null) { // we need at least a factory
                String artifact = node.getAttribute("artifact-id");
                String out      = node.getAttribute("out");
                String name     = node.getAttribute("facet");
                String ids      = node.getAttribute("ids");
                String displayname = node.getAttribute("name");
                String debugAttributeValues = "";
                for (String attr: node.getAttributes()) {
                    debugAttributeValues += ("[" + attr +": "
                        + node.getAttributeAsString(attr) + "] ");
                }
                GWT.log("DatacageWidget.collectToLoad, attributes are "
                    + debugAttributeValues);

                toLoad.add(artifact,
                     factory,
                     out,
                     name,
                     ids,
                     displayname);
            }
            TreeNode [] children = tree.getChildren(node);
            if (children != null) {
                for (TreeNode child: children) {
                    stack.push(child);
                }
            }
        }
    }


    /** Get meta-data and populate tree with it. */
    protected void triggerTreeBuilding() {
        Config config = Config.getInstance();
        String locale = config.getLocale();

        String artifactId = artifact.getUuid();
        String userId     = (user != null) ? user.identifier() : null;

        metaDataService.getMetaData(
            locale,
            artifactId,
            userId,
            outs,
            parameters,
            new AsyncCallback<DataCageTree>() {
                @Override
                public void onFailure(Throwable caught) {
                    GWT.log("Could not load meta data.");
                    SC.warn(caught.getMessage());
                }

                @Override
                public void onSuccess(DataCageTree dcTree) {
                    GWT.log("Successfully loaded meta data.");
                    IdGenerator idGenerator = new IdGenerator();
                    DataCageNode dcRoot = dcTree.getRoot();
                    TreeNode root = buildRecursiveChildren(
                        dcRoot, idGenerator);
                    tree.setRoot(root);

                    TreeNode[] nodes = tree.getChildren(root);
                    for (TreeNode node: nodes) {
                        if (node.getAttribute("factory") == null &&
                                !tree.hasChildren(node)) {
                            node.setIsFolder(true);
                        }
                    }

                    if (idGenerator.current() < MAX_OPEN) {
                        tree.openAll();
                    }
                    treeGrid.setData(tree);
                }
            });
    }

    private static final class IdGenerator {
        protected int current;

        public IdGenerator() {
        }

        public int next() {
            return current++;
        }

        public int current() {
            return current;
        }
    } // class IdGenerator

    private String i18n(String s) {
        if (!(s.startsWith("${") && s.endsWith("}"))) {
            return s;
        }

        s = s.substring(2, s.length()-1);

        try {
            return messages.getString(s);
        }
        catch (MissingResourceException mre) {
            GWT.log("cannot find i18n for + '" + s + "'");
            return s;
        }
    }

    protected TreeNode buildRecursiveChildren(
        DataCageNode   node,
        IdGenerator    idGenerator
    ) {
        TreeNode tn = new TreeNode();
        tn.setAttribute("id", idGenerator.next());

        List<DataCageNode> children = node.getChildren();

        if (children != null) {
            TreeNode [] tns = new TreeNode[children.size()];
            for (int i = 0; i < tns.length; ++i) {
                DataCageNode child = children.get(i);
                tns[i] = buildRecursiveChildren(child, idGenerator);
            }
            tn.setAttribute("children-nodes", tns);
        }

        tn.setAttribute("name", i18n(node.getDescription()));
        tn.setAttribute("facet", node.getName());

        AttrList attrs = node.getAttributes();
        if (attrs != null) {
            for (int i = 0, N = attrs.size(); i < N; ++i) {
                String key   = attrs.getKey(i);
                String value = attrs.getValue(i);
                tn.setAttribute(key, value);
            }
        }

        return tn;
    }
}
// vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :

http://dive4elements.wald.intevation.org