<commit_before>/*
 * This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
 *
 * Copyright 2008-2013 Geosparc nv, http://www.geosparc.com/, Belgium.
 *
 * The program is available in open source according to the GNU Affero
 * General Public License. All contributions in this program are covered
 * by the Geomajas Contributors License Agreement. For full licensing
 * details, see LICENSE.txt in the project root.
 */

package org.geomajas.widget.layer.client.widget;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.geomajas.annotation.Api;
import org.geomajas.configuration.client.ClientToolInfo;
import org.geomajas.configuration.client.ClientVectorLayerInfo;
import org.geomajas.gwt.client.Geomajas;
import org.geomajas.gwt.client.action.ToolbarBaseAction;
import org.geomajas.gwt.client.action.layertree.LayerTreeAction;
import org.geomajas.gwt.client.action.layertree.LayerTreeModalAction;
import org.geomajas.gwt.client.action.layertree.LayerTreeRegistry;
import org.geomajas.gwt.client.map.event.LayerChangedHandler;
import org.geomajas.gwt.client.map.event.LayerFilteredEvent;
import org.geomajas.gwt.client.map.event.LayerFilteredHandler;
import org.geomajas.gwt.client.map.event.LayerLabeledEvent;
import org.geomajas.gwt.client.map.event.LayerShownEvent;
import org.geomajas.gwt.client.map.event.LayerStyleChangeEvent;
import org.geomajas.gwt.client.map.event.LayerStyleChangedHandler;
import org.geomajas.gwt.client.map.layer.Layer;
import org.geomajas.gwt.client.map.layer.RasterLayer;
import org.geomajas.gwt.client.map.layer.VectorLayer;
import org.geomajas.gwt.client.util.UrlBuilder;
import org.geomajas.gwt.client.widget.MapWidget;
import org.geomajas.sld.FeatureTypeStyleInfo;
import org.geomajas.sld.RuleInfo;
import org.geomajas.sld.UserStyleInfo;
import org.geomajas.widget.layer.client.LayerMessages;
import org.geomajas.widget.layer.client.util.LayerIconUtil;
import org.geomajas.widget.layer.configuration.client.ClientAbstractNodeInfo;
import org.geomajas.widget.layer.configuration.client.ClientBranchNodeInfo;
import org.geomajas.widget.layer.configuration.client.ClientLayerNodeInfo;
import org.geomajas.widget.layer.configuration.client.ClientLayerTreeInfo;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.HandlerRegistration;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.SelectionType;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.ImgButton;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.LayoutSpacer;
import com.smartgwt.client.widgets.tree.TreeGrid;
import com.smartgwt.client.widgets.tree.TreeNode;
import com.smartgwt.client.widgets.tree.events.LeafClickEvent;

/**
 * A layertree widget with combined legend per layer.
 * 
 * @author <NAME>
 * @since 1.0.0
 */
@Api
public class CombinedLayertree extends LayerTreeBase {

	private static final String LEGEND_ICONS_PATH = "legendgraphic";

	private static final String LEGEND_ICONS_TYPE = ".png";

	private static final String SHOW_LAYERINFO_ICON = "[ISOMORPHIC]/geomajas/silk/cog.png";

	private static final LayerMessages MESSAGES = GWT.create(LayerMessages.class);

	private static final String EXPANDED_ATTR = "isExpanded";

	private final MapWidget mapWidget;

	private final List<HandlerRegistration> registrations = new ArrayList<HandlerRegistration>();

	protected LayerTreeTreeNode rollOverLayerTreeNode;

	private HashMap<VectorLayer, List<LayerTreeLegendItemNode>> legendIcons = 
		new HashMap<VectorLayer, List<LayerTreeLegendItemNode>>();

	public CombinedLayertree(final MapWidget mapWidget) {
		super(mapWidget);
		this.mapWidget = mapWidget;
		treeGrid.setShowRollOverCanvas(true);
	}

	public int getIconSize() {
		return treeGrid.getImageSize();
	}

	public void setIconSize(int iconSize) {
		treeGrid.setIconSize(iconSize);
	}

	/**
	 * Processes a treeNode (add it to the TreeGrid).
	 * 
	 * @param treeNode
	 *            The treeNode to process
	 * @param nodeRoot
	 *            The root node to which the treeNode has to be added
	 * @param refresh
	 *            True if the tree is refreshed (causing it to keep its expanded state)
	 */
	protected void processNode(final ClientAbstractNodeInfo treeNode, final TreeNode nodeRoot, final boolean refresh) {
		// Branches
		if (null != treeNode && treeNode instanceof ClientBranchNodeInfo) {
			String treeNodeLabel = ((ClientBranchNodeInfo) treeNode).getLabel();
			final TreeNode node = new TreeNode(treeNodeLabel);
			node.setAttribute(EXPANDED_ATTR, ((ClientBranchNodeInfo) treeNode).isExpanded());
			tree.add(node, nodeRoot);

			// treeNodes
			List<ClientAbstractNodeInfo> children = treeNode.getTreeNodes();
			for (ClientAbstractNodeInfo newNode : children) {
				processNode(newNode, node, refresh);
			}
			// Leafs
		} else if (null != treeNode && treeNode instanceof ClientLayerNodeInfo) {
			if (treeNode instanceof ClientLayerNodeInfo) {
				Layer<?> layer = mapModel.getLayer(((ClientLayerNodeInfo) treeNode).getLayerId());
				// Ignore layers that are not available in the map
				if (layer != null) {
					LayerTreeLegendNode ltln = new LayerTreeLegendNode(this.tree, layer);
					tree.add(ltln, nodeRoot);
					ltln.init();
				}
			}
		}
	}

	/**
	 * When a legendItem is selected, select the layer instead.
	 * 
	 * @param event
	 *            event
	 */
	public void onLeafClick(LeafClickEvent event) {
		LayerTreeTreeNode layerTreeNode;
		if (event.getLeaf() instanceof LayerTreeLegendItemNode) {
			layerTreeNode = ((LayerTreeLegendItemNode) event.getLeaf()).parent;
			treeGrid.deselectRecord(event.getLeaf());
			treeGrid.selectRecord(layerTreeNode);
		} else {
			layerTreeNode = (LayerTreeTreeNode) event.getLeaf();
		}

		// -- update model
		mapModel.selectLayer(layerTreeNode.getLayer());
	}

	// ----------------------------------------------------------

	/**
	 * Node with legend for LayerNode.
	 */
	public class LayerTreeLegendNode extends LayerTreeTreeNode {

		public LayerTreeLegendNode(RefreshableTree tree, Layer<?> layer) {
			super(tree, layer);
		}

		public void init() {
			if (layer instanceof VectorLayer) {
				VectorLayer vl = (VectorLayer) layer;
				ArrayList<LayerTreeLegendItemNode> nodeList = new ArrayList<LayerTreeLegendItemNode>();
				legendIcons.put(vl, nodeList);
				ClientVectorLayerInfo layerInfo = vl.getLayerInfo();

				// For vector layer; loop over the style definitions:
				UserStyleInfo userStyle = layerInfo.getNamedStyleInfo().getUserStyle();
				FeatureTypeStyleInfo info = userStyle.getFeatureTypeStyleList().get(0);
				for (int i = 0; i < info.getRuleList().size(); i++) {
					RuleInfo rule = info.getRuleList().get(i);
					// use title if present, name if not
					String title = (rule.getTitle() != null ? rule.getTitle() : rule.getName());
					// fall back to style name
					if (title == null) {
						title = layerInfo.getNamedStyleInfo().getName();
					}

					LayerTreeLegendItemNode tn = new LayerTreeLegendItemNode(this, vl, i, title);
					nodeList.add(tn);
					tree.add(tn, this);
				}

			} else if (layer instanceof RasterLayer) {
				RasterLayer rl = (RasterLayer) layer;
				LayerTreeLegendItemNode tn = new LayerTreeLegendItemNode(this, rl,
						LayerIconUtil.getSmallLayerIconUrl(rl));
				tree.add(tn, this);
			}
		}
	}

	/**
	 * Node which displays a legend icon + description.
	 */
	public class LayerTreeLegendItemNode extends LayerTreeTreeNode {

		private LayerTreeLegendNode parent;

		private int ruleIndex;

		// rasterlayer
		public LayerTreeLegendItemNode(LayerTreeLegendNode parent, RasterLayer layer, String rasterIconUrl) {
			super(parent.tree, parent.layer);
			this.parent = parent;
			setTitle(layer.getLabel());
			setName(parent.getAttribute("id") + "_legend");
			if (rasterIconUrl != null) {
				setIcon(rasterIconUrl);
			} else {
				UrlBuilder url = new UrlBuilder(Geomajas.getDispatcherUrl());
				url.addPath(LEGEND_ICONS_PATH);
				url.addPath(layer.getServerLayerId() + LEGEND_ICONS_TYPE);
				setIcon(url.toString());
			}
		}

		// vectorlayer
		public LayerTreeLegendItemNode(LayerTreeLegendNode parent, VectorLayer layer, int ruleIndex, String title) {
			super(parent.tree, parent.layer);
			this.parent = parent;
			setTitle(title);
			updateStyle(layer);
		}

		public void updateStyle(VectorLayer layer) {
			String name = layer.getLayerInfo().getNamedStyleInfo().getName();
			setName(name + "_" + ruleIndex);
			UrlBuilder url = new UrlBuilder(Geomajas.getDispatcherUrl());
			url.addPath(LEGEND_ICONS_PATH);
			url.addPath(layer.getServerLayerId());
			url.addPath(name);
			url.addPath(ruleIndex + ".png");
			setIcon(url.toString());
		}

		@Override
		public void updateIcon() {
			// leave my icons alone!
		}

		public LayerTreeLegendNode getParent() {
			return parent;
		}<commit_msg>GLW-10 Style change events are not handled<commit_after>/*
 * This is part of Geomajas, a GIS framework, http://www.geomajas.org/.
 *
 * Copyright 2008-2013 Geosparc nv, http://www.geosparc.com/, Belgium.
 *
 * The program is available in open source according to the GNU Affero
 * General Public License. All contributions in this program are covered
 * by the Geomajas Contributors License Agreement. For full licensing
 * details, see LICENSE.txt in the project root.
 */

package org.geomajas.widget.layer.client.widget;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import org.geomajas.annotation.Api;
import org.geomajas.configuration.client.ClientToolInfo;
import org.geomajas.configuration.client.ClientVectorLayerInfo;
import org.geomajas.gwt.client.Geomajas;
import org.geomajas.gwt.client.action.ToolbarBaseAction;
import org.geomajas.gwt.client.action.layertree.LayerTreeAction;
import org.geomajas.gwt.client.action.layertree.LayerTreeModalAction;
import org.geomajas.gwt.client.action.layertree.LayerTreeRegistry;
import org.geomajas.gwt.client.map.event.LayerChangedHandler;
import org.geomajas.gwt.client.map.event.LayerFilteredEvent;
import org.geomajas.gwt.client.map.event.LayerFilteredHandler;
import org.geomajas.gwt.client.map.event.LayerLabeledEvent;
import org.geomajas.gwt.client.map.event.LayerShownEvent;
import org.geomajas.gwt.client.map.event.LayerStyleChangeEvent;
import org.geomajas.gwt.client.map.event.LayerStyleChangedHandler;
import org.geomajas.gwt.client.map.layer.Layer;
import org.geomajas.gwt.client.map.layer.RasterLayer;
import org.geomajas.gwt.client.map.layer.VectorLayer;
import org.geomajas.gwt.client.util.UrlBuilder;
import org.geomajas.gwt.client.widget.MapWidget;
import org.geomajas.sld.FeatureTypeStyleInfo;
import org.geomajas.sld.RuleInfo;
import org.geomajas.sld.UserStyleInfo;
import org.geomajas.widget.layer.client.LayerMessages;
import org.geomajas.widget.layer.client.util.LayerIconUtil;
import org.geomajas.widget.layer.configuration.client.ClientAbstractNodeInfo;
import org.geomajas.widget.layer.configuration.client.ClientBranchNodeInfo;
import org.geomajas.widget.layer.configuration.client.ClientLayerNodeInfo;
import org.geomajas.widget.layer.configuration.client.ClientLayerTreeInfo;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.shared.HandlerRegistration;
import com.smartgwt.client.types.Alignment;
import com.smartgwt.client.types.SelectionType;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.IButton;
import com.smartgwt.client.widgets.ImgButton;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.LayoutSpacer;
import com.smartgwt.client.widgets.tree.TreeGrid;
import com.smartgwt.client.widgets.tree.TreeNode;
import com.smartgwt.client.widgets.tree.events.LeafClickEvent;

/**
 * A layertree widget with combined legend per layer.
 * 
 * @author <NAME>
 * @since 1.0.0
 */
@Api
public class CombinedLayertree extends LayerTreeBase {

	private static final String LEGEND_ICONS_PATH = "legendgraphic";

	private static final String LEGEND_ICONS_TYPE = ".png";

	private static final String SHOW_LAYERINFO_ICON = "[ISOMORPHIC]/geomajas/silk/cog.png";

	private static final LayerMessages MESSAGES = GWT.create(LayerMessages.class);

	private static final String EXPANDED_ATTR = "isExpanded";

	private final MapWidget mapWidget;

	private final List<HandlerRegistration> registrations = new ArrayList<HandlerRegistration>();

	protected LayerTreeTreeNode rollOverLayerTreeNode;

	private HashMap<VectorLayer, List<LayerTreeLegendItemNode>> legendIcons = 
		new HashMap<VectorLayer, List<LayerTreeLegendItemNode>>();

	public CombinedLayertree(final MapWidget mapWidget) {
		super(mapWidget);
		this.mapWidget = mapWidget;
		treeGrid.setShowRollOverCanvas(true);
	}

	public int getIconSize() {
		return treeGrid.getImageSize();
	}

	public void setIconSize(int iconSize) {
		treeGrid.setIconSize(iconSize);
	}

	/**
	 * Processes a treeNode (add it to the TreeGrid).
	 * 
	 * @param treeNode
	 *            The treeNode to process
	 * @param nodeRoot
	 *            The root node to which the treeNode has to be added
	 * @param refresh
	 *            True if the tree is refreshed (causing it to keep its expanded state)
	 */
	protected void processNode(final ClientAbstractNodeInfo treeNode, final TreeNode nodeRoot, final boolean refresh) {
		// Branches
		if (null != treeNode && treeNode instanceof ClientBranchNodeInfo) {
			String treeNodeLabel = ((ClientBranchNodeInfo) treeNode).getLabel();
			final TreeNode node = new TreeNode(treeNodeLabel);
			node.setAttribute(EXPANDED_ATTR, ((ClientBranchNodeInfo) treeNode).isExpanded());
			tree.add(node, nodeRoot);

			// treeNodes
			List<ClientAbstractNodeInfo> children = treeNode.getTreeNodes();
			for (ClientAbstractNodeInfo newNode : children) {
				processNode(newNode, node, refresh);
			}
			// Leafs
		} else if (null != treeNode && treeNode instanceof ClientLayerNodeInfo) {
			if (treeNode instanceof ClientLayerNodeInfo) {
				Layer<?> layer = mapModel.getLayer(((ClientLayerNodeInfo) treeNode).getLayerId());
				// Ignore layers that are not available in the map
				if (layer != null) {
					LayerTreeLegendNode ltln = new LayerTreeLegendNode(this.tree, layer);
					tree.add(ltln, nodeRoot);
					ltln.init();
				}
			}
		}
	}

	/**
	 * When a legendItem is selected, select the layer instead.
	 * 
	 * @param event
	 *            event
	 */
	public void onLeafClick(LeafClickEvent event) {
		LayerTreeTreeNode layerTreeNode;
		if (event.getLeaf() instanceof LayerTreeLegendItemNode) {
			layerTreeNode = ((LayerTreeLegendItemNode) event.getLeaf()).parent;
			treeGrid.deselectRecord(event.getLeaf());
			treeGrid.selectRecord(layerTreeNode);
		} else {
			layerTreeNode = (LayerTreeTreeNode) event.getLeaf();
		}

		// -- update model
		mapModel.selectLayer(layerTreeNode.getLayer());
	}

	// ----------------------------------------------------------

	/**
	 * Node with legend for LayerNode.
	 */
	public class LayerTreeLegendNode extends LayerTreeTreeNode {

		public LayerTreeLegendNode(RefreshableTree tree, Layer<?> layer) {
			super(tree, layer);
		}

		public void init() {
			if (layer instanceof VectorLayer) {
				VectorLayer vl = (VectorLayer) layer;
				ArrayList<LayerTreeLegendItemNode> nodeList = new ArrayList<LayerTreeLegendItemNode>();
				legendIcons.put(vl, nodeList);
				ClientVectorLayerInfo layerInfo = vl.getLayerInfo();

				// For vector layer; loop over the style definitions:
				UserStyleInfo userStyle = layerInfo.getNamedStyleInfo().getUserStyle();
				FeatureTypeStyleInfo info = userStyle.getFeatureTypeStyleList().get(0);
				for (int i = 0; i < info.getRuleList().size(); i++) {
					RuleInfo rule = info.getRuleList().get(i);
					// use title if present, name if not
					String title = (rule.getTitle() != null ? rule.getTitle() : rule.getName());
					// fall back to style name
					if (title == null) {
						title = layerInfo.getNamedStyleInfo().getName();
					}

					LayerTreeLegendItemNode tn = new LayerTreeLegendItemNode(this, vl, i, title);
					nodeList.add(tn);
					tree.add(tn, this);
				}

			} else if (layer instanceof RasterLayer) {
				RasterLayer rl = (RasterLayer) layer;
				LayerTreeLegendItemNode tn = new LayerTreeLegendItemNode(this, rl,
						LayerIconUtil.getSmallLayerIconUrl(rl));
				tree.add(tn, this);
			}
		}
	}

	/**
	 * Node which displays a legend icon + description.
	 */
	public class LayerTreeLegendItemNode extends LayerTreeTreeNode {

		private LayerTreeLegendNode parent;

		private int ruleIndex;

		// rasterlayer
		public LayerTreeLegendItemNode(LayerTreeLegendNode parent, RasterLayer layer, String rasterIconUrl) {
			super(parent.tree, parent.layer);
			this.parent = parent;
			setTitle(layer.getLabel());
			setName(parent.getAttribute("id") + "_legend");
			if (rasterIconUrl != null) {
				setIcon(rasterIconUrl);
			} else {
				UrlBuilder url = new UrlBuilder(Geomajas.getDispatcherUrl());
				url.addPath(LEGEND_ICONS_PATH);
				url.addPath(layer.getServerLayerId() + LEGEND_ICONS_TYPE);
				setIcon(url.toString());
			}
		}

		// vectorlayer
		public LayerTreeLegendItemNode(LayerTreeLegendNode parent, VectorLayer layer, int ruleIndex, String title) {
			super(parent.tree, parent.layer);
			this.ruleIndex = ruleIndex;
			this.parent = parent;
			setTitle(title);
			updateStyle(layer);
		}

		public void updateStyle(VectorLayer layer) {
			String name = layer.getLayerInfo().getNamedStyleInfo().getName();
			setName(name + "_" + ruleIndex);
			UrlBuilder url = new UrlBuilder(Geomajas.getDispatcherUrl());
			url.addPath(LEGEND_ICONS_PATH);
			url.addPath(layer.getServerLayerId());
			url.addPath(name);
			url.addPath(ruleIndex + ".png");
			setIcon(url.toString());
		}

		@Override
		public void updateIcon() {
			// leave my icons alone!
		}

		public LayerTreeLegendNode getParent() {
			return parent;
		}
{'fraction_non_alphanumeric': 0.11303395399780942, 'fraction_numerical': 0.0015334063526834612, 'mean_word_length': 4.381962864721485, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3757240', 'n_tokens_mistral': 6381, 'n_tokens_neox': 5632, 'n_words': 1200}