eu.limetri.client.mapviewer.nb.jxmap.map.LegendPanel 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.map;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;
import net.miginfocom.swing.MigLayout;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.painter.CompoundPainter;
import org.jdesktop.swingx.painter.MattePainter;
import org.jdesktop.swingx.painter.PinstripePainter;
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.NbBundle;
import eu.limetri.client.mapviewer.api.Layer;
import eu.limetri.client.mapviewer.api.LegendPalette;
import eu.limetri.client.mapviewer.api.RangedLegendPalette;
import eu.limetri.client.mapviewer.nb.jxmap.MapViewerTopComponent;
import javax.swing.JButton;
/**
* Panel for legends Listens to the {@link ExplorerManager} one of the parents
* of this component must have if a node is added or removed will use {@link MapViewerTopComponent#getMapDataManager()}
* to get the datamanager. Will check it's nodes if there is one and only one {@link LegendPalette}
* as pallete in the nodes. If this is the case will display the legend
*
* @author Merijn Zengers
*/
@NbBundle.Messages("LegendPanel title message=Legend")
//FIXME:Also listen to setPallete changes on layer
public class LegendPanel extends JXPanel {
private static final Dimension FULL_DIMENSION = new Dimension(200, 600);
private static final Dimension LEGEND_DIMENSION = new Dimension(120, 200);
private static final Color COLOR_SHADOW = new Color(150, 150, 150);
private static final Color COLOR_HIGH = new Color(210, 210, 210);
private static final Color COLOR_BASE = new Color(225, 225, 225);
private static final Dimension COLOR_BLOCK_DIMENSION = new Dimension(20, 20);
private final RootNodeListener rootNodeListener = new RootNodeListener();
private transient ExplorerManager explorer;
private JXPanel legendColorPanel;
private JXPanel legendActionPanel;
private final JLabel subtitleLabel = new JLabel("");
private List currentlyShowing = new ArrayList<>();
private final PaletteListener paletteListener = new PaletteListener();
private JButton rangeButton;
public LegendPanel() {
init();
}
private void init() {
setLayout(new BorderLayout());
setOpaque(false);
JPanel mainPanel = new JPanel();
mainPanel.setOpaque(false);
mainPanel.setPreferredSize(FULL_DIMENSION);
mainPanel.setLayout(new MigLayout("wrap, align right"));
JXPanel legendTitlePanel = new JXPanel(new MigLayout("wrap 2, insets 0 0 0 0"));
legendTitlePanel.setBackground(COLOR_HIGH);
legendTitlePanel.setDoubleBuffered(true);
legendTitlePanel.setAlpha(.8f);
legendTitlePanel.add(new JLabel(Bundle.LegendPanel_title_message()));
subtitleLabel.setHorizontalAlignment(SwingConstants.TRAILING);
legendTitlePanel.add(subtitleLabel);
//TODO: action/button panel
// allow customization of range (and maybe later unit as well)
legendActionPanel = new JXPanel();
legendActionPanel.setLayout(new MigLayout("wrap 2, insets 0 0 0 0"));
legendActionPanel.setBackground(COLOR_HIGH);
legendActionPanel.setAlpha(.8f);
rangeButton = new JButton(Bundle.range_settings_action_name());
rangeButton.setEnabled(false);
legendActionPanel.add(rangeButton);
legendColorPanel = new JXPanel();
legendColorPanel.setLayout(new MigLayout("wrap 2, insets 0 0 0 0"));
legendColorPanel.setBackground(COLOR_HIGH);
legendColorPanel.setDoubleBuffered(true);
legendColorPanel.setAlpha(.8f);
PinstripePainter stripes = new PinstripePainter();
stripes.setPaint(new Color(1.0f, 1.0f, 1.0f, 0.17f));
stripes.setSpacing(5.0);
MattePainter matte = new MattePainter(COLOR_BASE);
JXPanel legendMainPanel = new JXPanel(new MigLayout("wrap"));
legendMainPanel.setBorder(new BevelBorder(BevelBorder.RAISED, COLOR_HIGH, COLOR_SHADOW));
legendMainPanel.setBackground(COLOR_HIGH);
legendMainPanel.setDoubleBuffered(true);
legendMainPanel.setAlpha(.8f);
legendMainPanel.setPreferredSize(LEGEND_DIMENSION);
legendMainPanel.setBackgroundPainter(new CompoundPainter(matte, stripes));
legendMainPanel.add(legendTitlePanel);
legendMainPanel.add(legendActionPanel);
legendMainPanel.add(legendColorPanel);
mainPanel.add(legendMainPanel);
this.add(mainPanel, BorderLayout.CENTER);
this.setVisible(false);
}
// package private for mocking
void setSubtitle(String s) {
subtitleLabel.setText(s);
}
@Override
public void addNotify() {
super.addNotify();
getExplorerManager().getRootContext().addNodeListener(rootNodeListener);
}
ExplorerManager getExplorerManager() {
if (explorer == null) {
explorer = ExplorerManager.find(this);
assert explorer != null : "This component needs a parent wich is an ExplorerManager.Provider";
}
return explorer;
}
/**
* Activates the legend
Will set the visibility of this component
* to true Will add a property change listener on the palette it is
* currently showing
*
* @param legendPalette
*/
void activateLegend(List legendPalettes) {
this.setVisible(true);
currentlyShowing = legendPalettes;
updateLegend();
//Register listeners
for (LegendPalette palette : legendPalettes) {
palette.addPropertyChangeListener(paletteListener);
}
}
/**
* Deactivate the legend
Will set this components visibility to
* false if this component is currently showing a palette will
* unregister property change listener on it
*
* @param legendPalette
*/
void deactivateLegend(List legendPalettes) {
this.setVisible(false);
//Check if we are not showing if we are unregister listeners
if (legendPalettes != null) {
for (LegendPalette palette : legendPalettes) {
palette.removePropertyChangeListener(paletteListener);
}
currentlyShowing.clear();
}
}
/**
* Updates the legend Adds all the Legend colors and descriptions to the
* legend
*/
void updateLegend() {
if (currentlyShowing != null && !currentlyShowing.isEmpty()) {
legendColorPanel.removeAll();
for (LegendPalette.Entry legendColor : currentlyShowing.iterator().next().getEntries()) {
JLabel colorLabel = new JLabel("#");
colorLabel.setBackground(legendColor.getColor());
colorLabel.setForeground(legendColor.getColor());
colorLabel.setPreferredSize(COLOR_BLOCK_DIMENSION);
legendColorPanel.add(colorLabel);
legendColorPanel.add(new JLabel(legendColor.getDescription()));
}
repaint();
revalidate();
}
}
/**
* Checks if the nodes currently managed have a LegendPalette
*/
final void palettesChanged() {
//First clear everything
deactivateLegend(currentlyShowing);
//Find out if there are LegendPalette(s) in the lookup of the nodes
Set palettes = new HashSet<>();
Node[] existingNodes = getExplorerManager().getRootContext().getChildren().getNodes();
List possibleShowing = new ArrayList<>();
for (Node existingNode : existingNodes) {
LegendPalette palette = getLegendPaletteFromNode(existingNode);
if (palette != null) {
palettes.add(palette);
possibleShowing.add(palette);
} else {
//There is a node without a legend palette we cannot show
return;
}
}
//Only activate if there is only one unique LegendPalette in the found nodes
if (palettes.size() == 1) {
String unit = palettes.iterator().next().getUnit();
setSubtitle(unit == null ? null : "(" + unit+ ")");
activateLegend(possibleShowing);
updateRangeButton(possibleShowing);
}
}
void updateRangeButton(List possibleShowing) {
List> rangedPalettes = new ArrayList<>();
for (LegendPalette lp : possibleShowing) {
if (lp instanceof RangedLegendPalette) {
//FIXME: needs better test for generic type (or get rid of it altogether since we use double everywhere anyway)
try {
RangedLegendPalette rlp = (RangedLegendPalette) lp;
rangedPalettes.add(rlp);
} catch (ClassCastException cce) {}
}
}
if (!rangedPalettes.isEmpty()) {
rangeButton.setAction(new CustomRangeAction(rangedPalettes));
rangeButton.setEnabled(true);
} else {
rangeButton.setAction(null);
rangeButton.setEnabled(false);
}
}
/**
* Get the {@link legend palette from the node}
*
* @param node
* @return LegendPalette if found else null
*/
LegendPalette getLegendPaletteFromNode(Node node) {
Layer layer = node.getLookup().lookup(Layer.class);
if (layer != null && layer.getPalette() instanceof LegendPalette) {
return (LegendPalette) layer.getPalette();
}
return null;
}
private class PaletteListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent pce) {
palettesChanged();
}
}
/**
* listener for changes in nodes in the explorer manager
*/
private class RootNodeListener implements NodeListener {
@Override
public void childrenAdded(NodeMemberEvent ev) {
palettesChanged();
}
@Override
public void childrenRemoved(NodeMemberEvent ev) {
palettesChanged();
}
@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.");
}
}
}