eu.limetri.client.mapviewer.nb.jxmap.MapView Maven / Gradle / Ivy
/**
* Copyright (C) 2008-2012 AgroSense Foundation.
*
* AgroSense is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* There are special exceptions to the terms and conditions of the GPLv3 as it is applied to
* this software, see the FLOSS License Exception
* .
*
* AgroSense is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AgroSense. If not, see .
*/
package eu.limetri.client.mapviewer.nb.jxmap;
import java.awt.BorderLayout;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyVetoException;
import java.beans.VetoableChangeListener;
import java.util.HashSet;
import java.util.Set;
import javax.swing.ActionMap;
import javax.swing.JComponent;
import javax.swing.event.ListDataEvent;
import javax.swing.event.ListDataListener;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import org.openide.explorer.ExplorerManager;
import org.openide.explorer.ExplorerUtils;
import org.openide.nodes.Node;
import org.openide.nodes.NodeEvent;
import org.openide.nodes.NodeListener;
import org.openide.nodes.NodeMemberEvent;
import org.openide.nodes.NodeReorderEvent;
import org.openide.util.Lookup;
import org.openide.util.WeakListeners;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
import eu.limetri.client.mapviewer.api.Layer;
import eu.limetri.client.mapviewer.api.LayerDropTarget;
import eu.limetri.client.mapviewer.api.LayerInfo;
import eu.limetri.client.mapviewer.nb.jxmap.layerlist.LayerListController;
import eu.limetri.client.mapviewer.nb.jxmap.map.RootMapPanelController;
/**
* A view on data managed by an explorer manager. The nodes are represented on a
* map.
*
* Layer nodes are displayed in a layerlist LayerObject nodes contain the
* capability to be displayed on the map
*
*
*
* @author Timon Veenstra
*/
//FIXME cleanup
public class MapView extends JComponent implements Lookup.Provider {
private final LayerListController layerListController = new LayerListController();
private final RootMapPanelController mapPanelController = new RootMapPanelController(layerListController);
private final RootNodeListener rootNodeListener = new RootNodeListener();
// private final Map layerInfoMap = new HashMap();
/**
* Listener to nearly everything
*/
transient MapView.Listener managerListener;
/**
* weak variation of the listener for property change on the explorer
* manager
*/
transient PropertyChangeListener wlpc;
/**
* weak variation of the listener for vetoable change on the explorer
* manager
*/
transient VetoableChangeListener wlvc;
/**
* Explorer manager to work with. Is not null only if the component is
* showing in components hierarchy
*/
private transient ExplorerManager manager;
private final LookupManager lookupManager = new LookupManager();
/*
* Initilizes the view.
*/
@Override
public void addNotify() {
super.addNotify();
ExplorerManager em = ExplorerManager.find(this);
if (em != manager) {
if (manager != null) {
manager.removeVetoableChangeListener(wlvc);
manager.removePropertyChangeListener(wlpc);
}
manager = em;
lookupManager.update();
manager.getRootContext().addNodeListener(rootNodeListener);
Node root = manager.getRootContext();
if (root != null) {
addLayersForNewNodes(root);
}
manager.addVetoableChangeListener(wlvc = WeakListeners.vetoableChange(managerListener, manager));
manager.addPropertyChangeListener(wlpc = WeakListeners.propertyChange(managerListener, manager));
updateSelection();
}
}
public MapView() {
setLayout(new BorderLayout());
add(mapPanelController.getPanel(), BorderLayout.CENTER);
}
@Override
public void removeNotify() {
super.removeNotify();
}
public void addLayerDropTarget(LayerDropTarget dropTarget) {
mapPanelController.addLayerDropTarget(dropTarget);
}
@Override
public Lookup getLookup() {
return lookupManager.getProxy();
}
/**
* manages internal lookup and provides proxy
*/
private class LookupManager implements Lookup.Provider {
private Lookup internal = Lookup.EMPTY;
private Lookup proxy = Lookups.proxy(this);
public LookupManager() {
}
void update() {
internal = new ProxyLookup(ExplorerUtils.createLookup(manager, new ActionMap()), layerListController.getLookup());
}
@Override
public Lookup getLookup() {
return internal;
}
public Lookup getProxy() {
return proxy;
}
}
/**
* Recurse through nodes starting from root and add all LayerNodes (which
* are nodes with a Layer object in their lookup)
*
* @param root
*/
void addLayersForNewNodes(Node root) {
Layer layer = root.getLookup().lookup(Layer.class);
// only add nodes with a layer in their lookup
if (layer != null) {
//
// add a property change listener to the layer to respond to propertychanges (refresh)
//
// LayerPropertyChangeListener lpcl = new LayerPropertyChangeListener();
// layer.addPropertyChangeListener(lpcl);
//
// add a layernode listener to listen to changes in the layer node
//
LayerNodeListener layerNodeListener = new LayerNodeListener();
root.addNodeListener(layerNodeListener);
// sync the map explorer manager on the Layer
Layer.Sync.SyncMapExplorer(layer, manager);
//
// add the layer node to the mapPanel through its controller
//
LayerInfo layerInfo = root.getLookup().lookup(LayerInfo.class);
if (layerInfo == null) {
layerInfo = new LayerInfo();
}
mapPanelController.addLayerNode(root, layerInfo);
layerListController.addLayerNode(root, layerInfo);
}
//Expand the node so the children are created
root.getChildren().getNodes();
}
/**
* Removes layers related to provided nodes and childnodes from the map.
*
* @param layerNodes
*/
void removeLayers(Node... layerNodes) {
for (Node root : layerNodes) {
Layer layer = root.getLookup().lookup(Layer.class);
// only add nodes with a layer in their lookup
if (layer != null) {
mapPanelController.removeLayerNode(root);
layerListController.removeLayerNode(root);
}
}
}
/**
* listener for changes in nodes in the explorer manager
*/
private class RootNodeListener implements NodeListener {
@Override
public void childrenAdded(NodeMemberEvent ev) {
for (Node node : ev.getDelta()) {
addLayersForNewNodes(node);
}
}
@Override
public void childrenRemoved(NodeMemberEvent ev) {
removeLayers(ev.getDelta());
}
@Override
public void childrenReordered(NodeReorderEvent ev) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void nodeDestroyed(NodeEvent ev) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
throw new UnsupportedOperationException("Not supported yet.");
}
}
/**
* Listens to changes on a layernode
*
*/
private class LayerNodeListener implements NodeListener {
private void refreshAffectedLayerNodes(NodeMemberEvent ev) {
Set layerNodes = new HashSet<>();
for (Node node : ev.getDelta()) {
Node layerNode = Layer.Finder.findNodeWithLayer(node);
if (layerNode != null) {
layerNodes.add(layerNode);
}
}
for (Node node : layerNodes) {
mapPanelController.refreshLayerNode(node);
}
}
@Override
public void childrenAdded(NodeMemberEvent ev) {
refreshAffectedLayerNodes(ev);
}
@Override
public void childrenRemoved(NodeMemberEvent ev) {
refreshAffectedLayerNodes(ev);
}
@Override
public void childrenReordered(NodeReorderEvent ev) {
// not implemented
}
@Override
public void nodeDestroyed(NodeEvent ev) {
// not implemented
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
// not implemented
}
}
private final class Listener implements ListDataListener, ListSelectionListener, PropertyChangeListener,
VetoableChangeListener {
Listener() {
}
/**
* Implements
* ListDataListener
interface.
*/
@Override
public void intervalAdded(ListDataEvent evt) {
updateSelection();
}
/**
* Implements
* ListDataListener
.
*/
@Override
public void intervalRemoved(ListDataEvent evt) {
updateSelection();
}
/**
* Implemetns
* ListDataListener
.
*/
@Override
public void contentsChanged(ListDataEvent evt) {
updateSelection();
}
@Override
public void vetoableChange(PropertyChangeEvent evt)
throws PropertyVetoException {
if (ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
Node[] newNodes = (Node[]) evt.getNewValue();
if (!selectionAccept(newNodes)) {
throw new PropertyVetoException("", evt); // NOI18N
}
}
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
updateSelection();
}
}
@Override
public void valueChanged(ListSelectionEvent e) {
throw new UnsupportedOperationException("valueChanged");
// // forwarding TO E.M., so we won't listen to its cries for a while
// manager.removePropertyChangeListener(wlpc);
// manager.removeVetoableChangeListener(wlvc);
//
//// try {
//// selectionChanged(nodes, manager);
// } catch (PropertyVetoException ex) {
// // selection vetoed - restore previous selection
// updateSelection();
// } finally {
// manager.addPropertyChangeListener(wlpc);
// manager.addVetoableChangeListener(wlvc);
// }
}
}
private boolean selectionAccept(Node[] newNodes) {
throw new UnsupportedOperationException("selectionAccept");
}
private void updateSelection() {
//FIXME do we need to track selection here??
// base and map explorer manager as synced on the layer
// layer panel is listening to selection changes on the layer
// throw new UnsupportedOperationException("updateSelection");
// for (Node node:manager.getSelectedNodes()){
// LayerObject lo = node.getLookup().lookup(LayerObject.class);
// if (lo != null){
//
// }
// }
}
}