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.
/**
* Copyright (C) 2010-2012 Agrosense [email protected]
*
* Licensed under the Eclipse Public License - v 1.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain a
* copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package nl.cloudfarming.client.geoviewer.jxmap.map;
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.InputEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import nl.cloudfarming.client.geoviewer.Geographical;
import nl.cloudfarming.client.geoviewer.Layer;
import nl.cloudfarming.client.geoviewer.LayerInfo;
import nl.cloudfarming.client.geoviewer.jxmap.render.JXMapGeoTranslator;
import nl.cloudfarming.client.geoviewer.render.GeoTranslator;
import nl.cloudfarming.client.geoviewer.render.GeometricalWidget;
import nl.cloudfarming.client.geoviewer.render.IconWidget;
import nl.cloudfarming.client.util.ExplorerManagerUtil;
import org.jdesktop.swingx.JXMapViewer;
import org.jdesktop.swingx.JXPanel;
import org.netbeans.api.visual.action.ActionFactory;
import org.netbeans.api.visual.action.PopupMenuProvider;
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.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.Utilities;
/**
* 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 LayerPainter painter;
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;
public LayerPanel(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);
//TODO add connection layer for connection widgets
this.layer = node.getLookup().lookup(Layer.class);
assert this.layer != null;
this.id = id;
this.painter = new LayerPainter(node);
//turned off deprecated layer painter
// setBackgroundPainter(this.painter);
this.info = layerInfo;
this.mapViewer = mapViewer;
setLayout(new BorderLayout());
this.scene.setOpaque(false);
this.scene.addChild(this.mainLayer);
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();
}
});
// if (node.getChildren() == null || node.getChildren().getNodesCount() == 0) {
// LOGGER.warning("Empty ObjectLayer: The LayerNode did not contain any children.");
// } else {
// addObjects(node.getChildren().getNodes());
// }
add(view);
setActive(true);
}
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();
Widget widget = MapWidgetFactory.createWidgetFromNode(node, scene);
if (widget != null) {
addActions(widget, node);
}
Widget iconWidget = createIconWidget(node);
//TODO add same actions to the icon widget
//TODO create a connector between the icon and the geographical widget
if (widget != null) {
this.mainLayer.addChild(widget);
if (iconWidget != null) {
this.mainLayer.addChild(iconWidget);
this.scene.addObject(node, new Widget[]{widget, iconWidget});
} else {
this.scene.addObject(node, widget);
}
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);
}
}
}
}
/**
* Tries to create and returns an icon widget for the given node. No widget
* is created if the node's lookup doesn't contain a geographical or the
* geographical doesn't have an icon.
*
* @param The node (with a geographical in its lookup)
* @return The created icon widget or null if none created.
*/
private Widget createIconWidget(Node node) {
Geographical geographical = node.getLookup().lookup(Geographical.class);
GeoTranslator geoTranslator = new JXMapGeoTranslator(scene.getMapViewer());
Widget iconWidget = null;
if (geographical != null && geographical.getIconPath() != null) {
iconWidget = new IconWidget(geographical, layer.getPalette(), geoTranslator, scene);
}
return iconWidget;
}
private void addActions(Widget widget, final Node node) {
widget.getActions().addAction(scene.createObjectHoverAction());
widget.getActions().addAction(scene.createWidgetHoverAction());
widget.getActions().addAction(scene.createSelectAction());
widget.getActions().addAction(ActionFactory.createPopupMenuAction(new PopupMenuProvider() {
@Override
public JPopupMenu getPopupMenu(Widget widget, Point localLocation) {
JPopupMenu menu = Utilities.actionsToPopup(node.getActions(true), node.getLookup());
return menu;
}
}));
}
/**
* 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);
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.addObjectToSelection(changedObject, em);
}
//
// remove node from the selection if it is not removed
// and return to normal color
//
if (previousState.isSelected() && !newState.isSelected()) {
ExplorerManagerUtil.removeObjectFromSelection(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