Mercurial > dive4elements > river
view gwt-client/src/main/java/org/dive4elements/river/client/client/ui/DatacageWidget.java @ 9466:a41f5f7c0b4a
Reihenfolge fix
author | gernotbelger |
---|---|
date | Thu, 30 Aug 2018 12:11:08 +0200 |
parents | 84397da33d17 |
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.Collection; import java.util.List; import java.util.MissingResourceException; import java.util.Stack; import org.dive4elements.river.client.client.Config; import org.dive4elements.river.client.client.FLYSConstants; import org.dive4elements.river.client.client.event.DatacageDoubleClickHandler; import org.dive4elements.river.client.client.event.DatacageHandler; import org.dive4elements.river.client.client.services.MetaDataService; import org.dive4elements.river.client.client.services.MetaDataServiceAsync; import org.dive4elements.river.client.shared.model.AttrList; import org.dive4elements.river.client.shared.model.DataCageNode; import org.dive4elements.river.client.shared.model.DataCageTree; import org.dive4elements.river.client.shared.model.ToLoad; import org.dive4elements.river.client.shared.model.User; 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; // TODO: refactor, extract ~DataCageGrid /** * Display tree of, for example, previous calculations and allows * selection in order to access/clone these. */ public final class DatacageWidget extends VLayout { private static final FLYSConstants MSG = GWT.create(FLYSConstants.class); public static final int MAX_OPEN = 30; public interface DatacageFilter { boolean accept(DataCageNode node); } public static final DatacageFilter ACCEPT_ALL_FILTER = new DatacageFilter() { @Override public boolean accept(final DataCageNode node) { return true; } }; public static final DatacageFilter ACCEPT_NONE_FILTER = new DatacageFilter() { @Override public boolean accept(final DataCageNode node) { return false; } }; private final MetaDataServiceAsync metaDataService = GWT.create(MetaDataService.class); private final List<DatacageHandler> handlers = new ArrayList<DatacageHandler>(); private final List<DatacageDoubleClickHandler> doubleHandlers = new ArrayList<DatacageDoubleClickHandler>(); private final TreeGrid treeGrid; private Tree tree; private ToLoad toLoad; /** Layout to show spinning wheel of joy. */ private VLayout lockScreen; private DataCageTree dcTree; private DatacageFilter filter = ACCEPT_ALL_FILTER; private DatacageWidgetData data; public DatacageWidget(final DatacageWidgetData data) { this.data = data; this.toLoad = new ToLoad(); setWidth100(); this.treeGrid = new TreeGrid(); final String columnLabel = this.data.getColumnLabel(); if (columnLabel != null) this.treeGrid.setTreeFieldTitle(columnLabel); this.treeGrid.setLoadDataOnDemand(false); this.treeGrid.setWidth100(); this.treeGrid.setHeight100(); this.treeGrid.setShowRoot(false); this.treeGrid.setNodeIcon("[SKIN]/../blank.gif"); this.treeGrid.setShowConnectors(true); this.treeGrid.setLoadingMessage(MSG.databasket_loading()); this.treeGrid.setLoadingDataMessage(MSG.databasket_loading()); this.treeGrid.setEmptyMessage(MSG.databasket_empty()); this.treeGrid.setHoverMoveWithMouse(true); this.treeGrid.setCanHover(true); this.treeGrid.setShowHover(true); this.treeGrid.setHoverOpacity(75); this.treeGrid.setHoverWidth(120); this.treeGrid.setHoverCustomizer(new HoverCustomizer() { @Override public String hoverHTML(final Object value, final ListGridRecord record, final int rowNum, final int colNum) { if (record instanceof TreeNode) { final TreeNode hoveredTreeNode = (TreeNode) record; String info = hoveredTreeNode.getAttribute("info"); if (info == null) { info = hoveredTreeNode.getName(); } return info; } return "";// should not happen } }); this.treeGrid.addRecordDoubleClickHandler(new RecordDoubleClickHandler() { @Override public void onRecordDoubleClick(final RecordDoubleClickEvent event) { doubleClickedOnTree(event); } }); addMember(this.treeGrid); if (data.isShowButton()) addMember(createPlusButton()); triggerTreeBuilding(); } public void setFilter(final DatacageFilter filter) { assert (filter != null); this.filter = filter; if (this.dcTree != null) updateTree(this.dcTree); } public TreeGrid getTreeGrid() { return this.treeGrid; } /** Disable input, show spinning wheel of joy. */ private void lockUI() { this.lockScreen = ScreenLock.lockUI(this, this.lockScreen); } /** Enable input, remove grey, remove spinning wheel of joy. */ protected final void unlockUI() { ScreenLock.unlockUI(this, this.lockScreen); } /** * Sets whether more than one item can be selected. * * @param multi * if true, allow mutliple selections. */ public void setIsMutliSelectable(final boolean multi) { if (multi) { this.treeGrid.setSelectionType(SelectionStyle.MULTIPLE); } else { this.treeGrid.setSelectionType(SelectionStyle.SINGLE); } } /** * @param handler * Handler to be added (notified on add-action). */ public void addDatacageHandler(final DatacageHandler handler) { if (!this.handlers.contains(handler)) { this.handlers.add(handler); } } /** * @param h * Handler to be added (notified on Double click on node). */ public void addDatacageDoubleClickHandler(final DatacageDoubleClickHandler h) { if (!this.doubleHandlers.contains(h)) { this.doubleHandlers.add(h); } } /** * @param handler * Handler to remove from list. */ public void removeDatacageHandler(final DatacageHandler handler) { this.handlers.remove(handler); } public ToLoad getSelection() { // Reset content of toLoads. // FIXME: bad... instead we should react to selection events and update toLoad in this way. this.toLoad = new ToLoad(); if (this.treeGrid == null) return this.toLoad; final ListGridRecord[] selection = this.treeGrid.getSelectedRecords(); if (selection != null) { for (final ListGridRecord record : selection) { if (record instanceof TreeNode) { collectToLoads((TreeNode) record); } } } return this.toLoad; } public List<TreeNode> getPlainSelection() { final ListGridRecord[] selection = this.treeGrid.getSelectedRecords(); final List<TreeNode> nodes = new ArrayList<TreeNode>(); if (selection != null) { for (final ListGridRecord record : selection) { if (record instanceof TreeNode) { nodes.add((TreeNode) record); } } } return nodes; } /** * Returns the titles of selected items (if any). */ public String[] getSelectionTitles() { if (this.treeGrid == null) { return new String[] {}; } final ListGridRecord[] selection = this.treeGrid.getSelectedRecords(); if (selection == null) { return new String[] {}; } final List<String> titleList = new ArrayList<String>(); for (final 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. */ protected final void plusClicked() { if (!getSelection().isEmpty()) { fireToLoad(); } } private Button createPlusButton() { final Button plusBtn = new Button(MSG.datacageAdd()); plusBtn.addClickHandler(new ClickHandler() { @Override public void onClick(final ClickEvent event) { plusClicked(); } }); return plusBtn; } private void fireToLoad() { for (final DatacageHandler handler : this.handlers) { handler.toLoad(this.toLoad); } } /** Notify DatacageDoubleClickHandlers that a doubleclick happened. */ private void fireOnDoubleClick() { for (final DatacageDoubleClickHandler handler : this.doubleHandlers) { handler.onDoubleClick(this.toLoad); } } protected final void doubleClickedOnTree(final RecordDoubleClickEvent event) { final 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). */ private void collectToLoads(final TreeNode root1) { final Stack<TreeNode> stack = new Stack<TreeNode>(); stack.push(root1); while (!stack.isEmpty()) { final TreeNode node = stack.pop(); final String factory = node.getAttribute("factory"); if (factory != null) { // we need at least a factory final String artifact = node.getAttribute("artifact-id"); final String out = node.getAttribute("out"); final String name = node.getAttribute("facet"); final String ids = node.getAttribute("ids"); final String displayname = node.getAttribute("name"); final String targetOut = node.getAttribute("target_out"); String debugAttributeValues = ""; for (final String attr : node.getAttributes()) { debugAttributeValues += ("[" + attr + ": " + node.getAttributeAsString(attr) + "] "); } GWT.log("DatacageWidget.collectToLoad, attributes are " + debugAttributeValues); this.toLoad.add(artifact, factory, out, name, ids, displayname, targetOut); } final TreeNode[] children = this.tree.getChildren(node); if (children != null) { for (final TreeNode child : children) { stack.push(child); } } } } /** Get meta-data and populate tree with it. */ private void triggerTreeBuilding() { lockUI(); final String locale = Config.getInstance().getLocale(); final String artifactId = this.data.getArtifact().getUuid(); final User user = this.data.getUser(); final String userId = user != null ? user.identifier() : null; final String outs = this.data.getOuts(); final String parameters = this.data.getParameters(); this.metaDataService.getMetaData(locale, artifactId, userId, outs, parameters, new AsyncCallback<DataCageTree>() { @Override public void onFailure(final Throwable caught) { GWT.log("Could not load meta data."); SC.warn(caught.getMessage()); unlockUI(); } @Override public void onSuccess(final DataCageTree dcData) { GWT.log("Successfully loaded meta data."); setDatacageData(dcData); unlockUI(); } }); } protected final void setDatacageData(final DataCageTree dcTree) { this.dcTree = dcTree; if (this.tree != null) this.tree.destroy(); updateTree(this.dcTree); } private void updateTree(final DataCageTree dcData) { this.tree = new Tree(); this.tree.setModelType(TreeModelType.CHILDREN); this.tree.setNameProperty("name"); this.tree.setIdField("id"); this.tree.setChildrenProperty("children-nodes"); this.tree.setShowRoot(false); final IdGenerator idGenerator = new IdGenerator(); final DataCageNode dcRoot = dcData.getRoot(); final TreeNode root = buildRecursiveChildren(dcRoot, idGenerator); if (root != null) { this.tree.setRoot(root); // FIXME: why is this necessary? an it only happens for the first level... final TreeNode[] nodes = this.tree.getChildren(root); for (final TreeNode node : nodes) { if (node.getAttribute("factory") == null && !this.tree.hasChildren(node)) node.setIsFolder(true); } } if (idGenerator.current() < MAX_OPEN) this.tree.openAll(); this.treeGrid.setData(this.tree); } private static final class IdGenerator { protected int current; public IdGenerator() { } public int next() { return this.current++; } public int current() { return this.current; } } // class IdGenerator private String i18n(final String s) { if (!(s.startsWith("${") && s.endsWith("}"))) return s; final String sub = s.substring(2, s.length() - 1); try { return MSG.getString(sub); } catch (final MissingResourceException mre) { GWT.log("cannot find i18n for + '" + sub + "'", mre); return sub; } } private TreeNode buildRecursiveChildren(final DataCageNode node, final IdGenerator idGenerator) { if (!this.filter.accept(node)) return null; final List<DataCageNode> children = node.getChildren(); final Collection<TreeNode> tns = new ArrayList<TreeNode>(); if (children != null && !children.isEmpty()) { for (final DataCageNode child : children) { final TreeNode childNode = buildRecursiveChildren(child, idGenerator); if (childNode != null) tns.add(childNode); } /* if we should have children, but all got filtered, hide this node as well */ if (tns.isEmpty()) return null; } final TreeNode tn = new TreeNode(); tn.setAttribute("id", idGenerator.next()); if (!tns.isEmpty()) tn.setAttribute("children-nodes", tns.toArray(new TreeNode[tns.size()])); tn.setAttribute("name", i18n(node.getDescription())); tn.setAttribute("facet", node.getName()); final AttrList attrs = node.getAttributes(); if (attrs != null) { for (int i = 0, N = attrs.size(); i < N; ++i) { final String key = attrs.getKey(i); final String value = attrs.getValue(i); tn.setAttribute(key, value); } } return tn; } }