/*
* Copyright Jan 15, 2010 John T. Langton
* email: jlangton at visitrend dot com
* www.visitrend.com
*
* License: GPLv2 or (at your option) any later GPL version
*
* This file is part of NDVis.
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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.
*
* There should be a copy of the GNU General Public License applied to
* NDVis in the file "NDVis-license" in the folder "license". If not, see
* <http://www.gnu.org/licenses/> or write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package com.visitrend.ndvis.image;
import com.visitrend.ndvis.colormapper.ColorMapper;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.EventListener;
import javax.swing.Scrollable;
import javax.swing.SwingConstants;
import javax.swing.event.EventListenerList;
import com.visitrend.ndvis.event.api.DataVisualizationChangedEvent;
import com.visitrend.ndvis.event.api.DataVisualizationListener;
import com.visitrend.ndvis.gui.spi.DataVisualization;
import com.visitrend.ndvis.model.DataInfo;
import com.visitrend.ndvis.model.DataInfoINF;
import java.awt.BorderLayout;
import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ScrollPaneConstants;
import org.openide.cookies.SaveCookie;
import org.openide.util.Lookup;
import org.openide.util.lookup.AbstractLookup;
import org.openide.util.lookup.InstanceContent;
import org.openide.util.lookup.Lookups;
import org.openide.util.lookup.ProxyLookup;
import org.openide.util.lookup.ServiceProvider;
import org.openide.windows.TopComponent;
import org.openide.windows.WindowManager;
import org.visitrend.ndvis.parameters.Parameters;
/**
* Class for displaying the actual visualization.
*
* @author John T. Langton - jlangton at visitrend dot com
*
*/
@TopComponent.Registration(mode = "editor", openAtStartup = true)
@TopComponent.Description(preferredID = "ImagePanel", persistenceType = TopComponent.PERSISTENCE_ONLY_OPENED)
@TopComponent.OpenActionRegistration(displayName = "ImagePanel", preferredID = "ImagePanel")
@ServiceProvider(service = DataVisualization.class)
public class ImagePanel extends TopComponent implements DataVisualization {
private static int count = 0;
EventListenerList listeners;
private AffineTransform atx;
private BufferedImage offScreenImage;
// flag for ParametersController to keep state on ImagePanel
private boolean paramControllerListening = true;
private boolean pounded = false;
private int defaultWidth = 1296, defaultHeight = 1296;
private SaveCookie impl;
// tracks what PlugIn is active - only one can be active at a
// time to respond to user mouse actions on images
private InstanceContent saveLookup;
private JScrollPane scrollPane;
private JPanel image;
private InstanceContent content = new InstanceContent();
private final DataInfoINF dataInfo = new DataInfo();
public ImagePanel() {
super();
init(new BufferedImage(defaultWidth, defaultHeight,
BufferedImage.TYPE_INT_RGB));
}
public ImagePanel(BufferedImage img) {
super();
init(img);
}
private void init(BufferedImage img) {
saveLookup = new InstanceContent();
associateLookup(new ProxyLookup(new AbstractLookup(content),
new AbstractLookup(saveLookup), Lookups.singleton(this)));
content.add(dataInfo);
final Parameters params = new Parameters(dataInfo, this);
content.add(params);
final ColorMapper cm = new ColorMapper(params, this);
content.add(cm);
if(img==null){
// it's important to call these ColorEngine method in this order
cm.getEngine().updateOrder();
cm.getEngine().updateBases();
img = new BufferedImage(cm.getEngine().getUpperXbound(),
cm.getEngine().getUpperYbound(), BufferedImage.TYPE_INT_RGB);
}
image = new ImagePane();
listeners = new EventListenerList();
atx = new AffineTransform();
resetImageManipulation();
image.setDoubleBuffered(false);
image.setBackground(Color.gray);
image.setForeground(Color.white);
image.setOpaque(true);
// Let the user scroll by dragging to outside the window.
image.setAutoscrolls(true);
// this will set more stuff and possibly resize
// this panel component
setOffScreenImage(img);
scrollPane = new JScrollPane(image);
scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
setLayout(new BorderLayout());
add(scrollPane, BorderLayout.CENTER);
add(params, BorderLayout.NORTH);
add(cm, BorderLayout.SOUTH);
count++;
setDisplayName("Image " + count);
WindowManager.getDefault().invokeWhenUIReady(new Runnable() {
@Override
public void run() {
params.setVisible(true);
cm.setVisible(true);
impl = Lookup.getDefault().lookup(SaveCookie.class);
}
});
}
public void addImagePanelListener(DataVisualizationListener listener) {
listeners.add(DataVisualizationListener.class, listener);
}
public void removeImagePanelListener(DataVisualizationListener listener) {
listeners.remove(DataVisualizationListener.class, listener);
}
public int getDefaultWidth() {
return defaultWidth;
}
public int getDefaultHeight() {
return defaultHeight;
}
public BufferedImage getOffScreenImage() {
return offScreenImage;
}
/**
* The BufferedImage parameter will serve as the "offscreen image" for this
* ImagePanel. It will be painted on the ImagePanel using any transforms in
* the {@link #paintComponent(Graphics)} method.
*
* The ImagePanel's size will always be set to the dimensions of the
* BufferedImage parameter.
*
* @param img
*/
public void setOffScreenImage(BufferedImage img) {
if (img == null) {
img = new BufferedImage(defaultWidth, defaultHeight,
BufferedImage.TYPE_INT_RGB);
}
this.offScreenImage = img;
// resize the panel to the size of the image
image.setPreferredSize(new Dimension(offScreenImage.getWidth(),
offScreenImage.getHeight()));
image.revalidate();
image.repaint();
fireOriginalImageChanged();
}
public void resetImageManipulation() {
atx.setToIdentity();
}
public AffineTransform getTransform() {
return atx;
}
public void setTransform(AffineTransform at) {
this.atx = at;
image.repaint();
}
public void recolor() {
repaint();
fireOriginalImageChanged();
}
// ****************************************************
// some flag stuff for other classes to keep state in
// here, there's probably a better way of doing this
// ****************************************************
public boolean isParamControllerListening() {
return paramControllerListening;
}
public void setParamControllerListening(boolean paramControllerListening) {
this.paramControllerListening = paramControllerListening;
}
public boolean isPounded() {
return pounded;
}
public void setPounded(boolean pounded) {
this.pounded = pounded;
}
/*
* ******************************************************
*
* Begin Scroling Stuff
*
* *****************************************************
*/
private class ImagePane extends JPanel implements Scrollable {
// scroll variables
private int maxScrollUnitIncrement = 20;
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g2 = (Graphics2D) g;
// erase the area
g.setColor(Color.white);
g.fillRect(0, 0, getWidth(), getHeight());
// Draw the image, given the transform, on this ImagePanel. Passing in
// the transform insures that we don't overwrite the transform of the
// Graphics object, which is also used to render GUI components and
// everything else (e.g. we don't want to zoom our buttons). I believe
// this actually sets the Graphics object transform while rendering the
// image then restores the previous transform, nice little helper
g2.drawImage(offScreenImage, atx, null);
}
public Dimension getPreferredScrollableViewportSize() {
return image.getPreferredSize();
}
public int getScrollableBlockIncrement(Rectangle visibleRect,
int orientation, int direction) {
if (orientation == SwingConstants.HORIZONTAL) {
return visibleRect.width - maxScrollUnitIncrement;
} else {
return visibleRect.height - maxScrollUnitIncrement;
}
}
public int getScrollableUnitIncrement(Rectangle visibleRect,
int orientation, int direction) {
// Get the progress position.
int currentPosition = 0;
if (orientation == SwingConstants.HORIZONTAL) {
currentPosition = visibleRect.x;
} else {
currentPosition = visibleRect.y;
}
// Return the number of pixels between currentPosition
// and the nearest tick mark in the indicated direction.
if (direction < 0) {
int newPosition = currentPosition
- (currentPosition / maxScrollUnitIncrement)
* maxScrollUnitIncrement;
return (newPosition == 0) ? maxScrollUnitIncrement : newPosition;
} else {
return ((currentPosition / maxScrollUnitIncrement) + 1)
* maxScrollUnitIncrement - currentPosition;
}
}
public boolean getScrollableTracksViewportWidth() {
return false;
}
public boolean getScrollableTracksViewportHeight() {
return false;
}
public void setmaxScrollUnitIncrement(int pixels) {
maxScrollUnitIncrement = pixels;
}
/**
* @return Returns the maxScrollUnitIncrement.
*/
public int getMaxScrollUnitIncrement() {
return maxScrollUnitIncrement;
}
/**
* @param maxScrollUnitIncrement
* The maxScrollUnitIncrement to set.
*/
public void setMaxScrollUnitIncrement(int maxScrollUnitIncrement) {
this.maxScrollUnitIncrement = maxScrollUnitIncrement;
}
/*
* *******************
*
* End Scrolling Stuff
*
* *******************
*/
}
// ------------------ Listener Methods ---------------------------
private void fireOriginalImageChanged() {
DataVisualizationChangedEvent event = new DataVisualizationChangedEvent(this);
EventListener[] list = listeners.getListeners(DataVisualizationListener.class);
for (int i = list.length - 1; i >= 0; --i) {
((DataVisualizationListener) list[i]).originalVisualizationChanged(event);
}
}
@Override
public TopComponent getTopComponent() {
return this;
}
@Override
public JComponent getImagePane() {
return image;
}
public void fireSaveStateChange(boolean modified) {
if (modified) {
//If the text is modified,
//we add SaveCookie impl to Lookup:
saveLookup.add(impl);
} else {
//Otherwise, we remove the SaveCookie impl from the lookup:
saveLookup.remove(impl);
}
}
}