nl.cloudfarming.client.geoviewer.jxmap.map.LayerPanel Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of geoviewer-jxmap Show documentation
Show all versions of geoviewer-jxmap Show documentation
AgroSense geoviewer JXMap implementation. Contains a map/geoviewer TopComponent based on the JXMap classes from swingx.
The newest version!
/**
* 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 nl.cloudfarming.client.geoviewer.jxmap.map;
import java.awt.*;
import java.awt.event.InputEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.*;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.SwingUtilities;
import nl.cloudfarming.client.geoviewer.*;
import nl.cloudfarming.client.geoviewer.render.GeometricalWidget;
import nl.cloudfarming.client.util.ExplorerManagerUtil;
import org.jdesktop.swingx.JXMapViewer;
import org.jdesktop.swingx.JXPanel;
import org.netbeans.api.visual.action.WidgetAction.WidgetMouseEvent;
import org.netbeans.api.visual.model.ObjectSceneEvent;
import org.netbeans.api.visual.model.ObjectSceneEventType;
import org.netbeans.api.visual.model.ObjectSceneListener;
import org.netbeans.api.visual.model.ObjectState;
import org.netbeans.api.visual.widget.LayerWidget;
import org.netbeans.api.visual.widget.Widget;
import org.openide.explorer.ExplorerManager;
import org.openide.nodes.*;
/**
* Layer panel containing objects object layers will be painted as images with a
* backgroundPainter by default when the layer is activated, the actual objects
* (widgets) will be painted over the background.
*
* @author Timon Veenstra
*/
public class LayerPanel extends JXPanel implements ObjectSceneListener, MapMouseListener, PropertyChangeListener {
private static final Logger LOGGER = Logger.getLogger("nl.cloudfarming.client.geoviewer.jxmap.map.LayerPanel");
private final MapScene scene;
private final JComponent view;
private boolean removingObjects = false;
private final Layer layer;
private final LayerInfo info;
private final JXMapViewer mapViewer; // needed to create widgets which need to be aware of the map
private final int id;
private final RootMapPanel.LayerGroup group = RootMapPanel.LayerGroup.LAYER_ACTIVE;
private boolean outgoingSelectionChange = false;
private boolean incomingSelectionChange = false;
//
// widget layer
private final LayerWidget mainLayer;
private final LayerWidget iconLayer;
public LayerPanel(final Node node, JXMapViewer mapViewer, int id, LayerInfo layerInfo) {
//
// initalize the scene, view and main layer which map objects are drawn on
//
this.scene = new MapScene(mapViewer);
this.view = scene.createView();
this.mainLayer = new LayerWidget(scene);
this.iconLayer = new LayerWidget(scene);
//TODO add connection layer for connection widgets
this.layer = node.getLookup().lookup(Layer.class);
assert this.layer != null;
this.id = id;
this.info = layerInfo;
this.mapViewer = mapViewer;
setLayout(new BorderLayout());
this.scene.setOpaque(false);
this.scene.addChild(this.mainLayer);
this.scene.addChild(this.iconLayer);
this.scene.setMaximumBounds(new Rectangle(0, 0, Integer.MAX_VALUE, Integer.MAX_VALUE));
LOGGER.finest("Adding the node");
addObject(node);
this.scene.addObjectSceneListener(this, ObjectSceneEventType.OBJECT_STATE_CHANGED, ObjectSceneEventType.OBJECT_SELECTION_CHANGED);
node.addNodeListener(new NodeListener() {
@Override
public void childrenAdded(NodeMemberEvent ev) {
final Node[] addedObjects = new Node[ev.getDelta().length];
int i = 0;
for (Node node : ev.getDelta()) {
addedObjects[i] = node;
i++;
}
addObjects(addedObjects);
}
@Override
public void childrenRemoved(NodeMemberEvent ev) {
final List removeObjects = new ArrayList<>();
removeObjects.addAll(Arrays.asList(ev.getDelta()));
removeObjects(removeObjects);
}
@Override
public void childrenReordered(NodeReorderEvent ev) {
}
@Override
public void nodeDestroyed(NodeEvent ev) {
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
}
});
setOpaque(false);
this.info.addPropertyChangeListener(LayerInfo.PROPERTY_VISIBLE, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
setVisible((Boolean) evt.getNewValue());
repaint();
}
});
this.info.addPropertyChangeListener(LayerInfo.PROPERTY_TRANSPARENCY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
setAlpha(info.getTransparency());
repaint();
}
});
this.info.addPropertyChangeListener(LayerInfo.PROPERTY_SHOW_ICONS, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
updateIcons();
repaint();
}
});
// if (node.getChildren() == null || node.getChildren().getNodesCount() == 0) {
// LOGGER.warning("Empty ObjectLayer: The LayerNode did not contain any children.");
// } else {
// addObjects(node.getChildren().getNodes());
// }
// listen for geometry creation, re-add node to create widgets:
if (layer instanceof SingleObjectLayer) {
layer.addPropertyChangeListener(SingleObjectLayer.PROP_GEOMETRY, new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt.getOldValue() == null && evt.getNewValue() != null) {
addObjects(new Node[]{node});
}
}
});
}
updateIcons();
add(view);
setActive(true);
}
private void updateIcons() {
iconLayer.setVisible(info.showIcons());
}
public static boolean isRightMouseButton(WidgetMouseEvent anEvent) {
return ((anEvent.getModifiers() & InputEvent.BUTTON3_MASK) == InputEvent.BUTTON3_MASK);
}
/**
* remove objects if remove is called from the event dispatch thread it ill
* be executed immediate, if not it will be scheduled on the dispatch
* thread.
*
* @param objects
*/
private void removeObjects(final List objects) {
if (EventQueue.isDispatchThread()) {
internalRemoveObjects(objects);
} else {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
internalRemoveObjects(objects);
}
});
}
}
private void internalRemoveObjects(List objects) {
assert objects != null;
removingObjects = true;
for (Node node : objects) {
List widgets = this.scene.findWidgets(node);
if (widgets != null) {
for (Widget w : widgets) {
mainLayer.removeChild(w);
}
this.scene.removeObject(node);
}
}
this.scene.validate();
removingObjects = false;
}
/**
* add objects if called from the event dispatch thread it ill be executed
* immediate, if not it will be scheduled on the dispatch thread.
*
* @param objects
*/
private void addObjects(final Node[] nodes) {
if (SwingUtilities.isEventDispatchThread()) {
internalAddObjects(nodes);
} else {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
internalAddObjects(nodes);
}
});
}
}
/**
* Direct add
*
* @param nodes
*/
private void internalAddObjects(final Node[] nodes) {
assert SwingUtilities.isEventDispatchThread();
for (Node node : nodes) {
addObject(node);
}
scene.validate();
}
/**
* Add an object to the panel. this method will add the object to the scene
* and create a widget attached to it.
*
* @param objectNode
*/
private void addObject(final Node node) {
assert SwingUtilities.isEventDispatchThread();
addMapActions(node);
Widget widget = MapWidgetFactory.createWidgetFromNode(node, scene);
if (widget != null) {
this.mainLayer.addChild(widget);
initPaletteChangeListener(node);
Widget iconWidget = MapWidgetFactory.createIconWidgetFromNode(node, scene);
//TODO create a connector between the icon and the geographical widget, see https://blogs.oracle.com/geertjan/entry/acceptprovider_connectprovider
//TODO icon and label should be in different widget
if (iconWidget != null) {
this.iconLayer.addChild(iconWidget);
this.scene.addObject(node, new Widget[]{widget, iconWidget});
} else {
this.scene.addObject(node, widget);
}
}
addMapOnlyChildNodes(node);
if (node.getChildren() != null && node.getChildren().getNodes().length > 0) {
LOGGER.finest("Node contains children, adding them recursively");
for (Node child : node.getChildren().getNodes()) {
addObject(child);
}
}
}
/**
* Checks if the given Node contains map only children and adds them to the
* panel.
*
* @param node The node (with or without a {@link MapOnlyNodeProvider} in
* its Lookup
*/
private void addMapOnlyChildNodes(Node node) {
MapOnlyNodeProvider parent = node.getLookup().lookup(MapOnlyNodeProvider.class);
if (parent != null) {
internalAddObjects(parent.getMapOnlyNodes());
}
}
/**
* Adds some map actions to the DynamicGeometrical in the lookup. These map
* actions can only be performed in the context of a map. Doesn't do
* anything if there is no DynamicGeographical in the lookup.
*
* @param node The node (with a DynamicGeometrical in its Lookup)
*/
private void addMapActions(Node node) {
// FIXME: actions are duplicated when the node is dragged to the map a second time (depends on collection in dyn.geom.impl.)
// FIXME: picklocation action keeps a reference to the mapviewer after the node is removed from map.
DynamicPoint point = node.getLookup().lookup(DynamicPoint.class);
if (point != null) {
point.addMapAction(PickLocationAction.create(node, scene));
}
}
/**
* activate or deactivate this layer when layer gets activated, the view
* will be made visible, painting all containing objects as widgets and
* providing interaction.
*
* @param active
*/
final void setActive(boolean active) {
//TODO since we no longer make distinction to active and passive layers we don't want to
// disable background painting.
// We might want to prevent doulbe painting of objects though..
// this.view.setVisible(active);
// setBackgroundPainter(active ? null : getPainter());
// this.view.repaint();
}
@Override
public void objectAdded(ObjectSceneEvent event, Object addedObject) {
}
@Override
public void objectRemoved(ObjectSceneEvent event, Object removedObject) {
}
@Override
public void objectStateChanged(ObjectSceneEvent event, Object changedObject, ObjectState previousState, ObjectState newState) {
// Widget widget = event.getObjectScene().findWidget(changedObject);
//
// LOGGER.log(Level.FINEST, "objectStateChanged for {0}", changedObject);
//
// if (widget != null) {
// if (!removingObjects
// && !incomingSelectionChange
// && changedObject instanceof Node) {
// Node node = (Node) changedObject;
//
// widget.setForeground(getLayer().getPalette().getColorForState(newState));
//
// outgoingSelectionChange = true;
// ExplorerManager em = ExplorerManager.find(this);
// //
// // add node to the selection
// //
// if (!previousState.isSelected() && newState.isSelected()) {
// ExplorerManagerUtil.setSelectedObject(changedObject, em);
// }
// outgoingSelectionChange = false;
// }
// }
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (!outgoingSelectionChange && ExplorerManager.PROP_SELECTED_NODES.equals(evt.getPropertyName())) {
Node[] newSelection = (Node[]) evt.getNewValue();
Set selection = new HashSet<>();
for (Node selected : newSelection) {
// only add to selection if the object is part of this scene
if (scene.findStoredObject(selected) != null) {
selection.add(selected);
}
}
incomingSelectionChange = true;
scene.setSelectedObjects(selection);
incomingSelectionChange = false;
view.repaint();
} else if (Layer.PROP_PALETTE.equals(evt.getPropertyName())) {
view.repaint();
}
}
@Override
public void selectionChanged(ObjectSceneEvent event, Set