/*
* Copyright (c) 2003-2012 Fred Hutchinson Cancer Research Center
*
* Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
*
* 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 org.fhcrc.cpl.toolbox.gui.chart;
import org.apache.log4j.Logger;
import org.fhcrc.cpl.toolbox.ApplicationContext;
import javax.swing.*;
import javax.swing.event.ChangeListener;
import javax.swing.event.ChangeEvent;
import javax.imageio.ImageIO;
import java.util.List;
import java.util.ArrayList;
import java.util.Date;
import java.awt.event.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/**
* display multiple charts in a panel. Supports saving of all charts at once, to a directory
*
* Only a tabbed display is supported at the moment
*/
public abstract class MultiChartDisplayPanel extends JPanel
{
protected static Logger _log =
Logger.getLogger(MultiChartDisplayPanel.class);
public static final int DEFAULT_WIDTH = 800;
public static final int DEFAULT_HEIGHT = 800;
public static MultiChartDisplayPanel _singletonInstance = null;
public static boolean _hiddenMode = false;
protected final List<PanelWithChart> chartPanels = new ArrayList<PanelWithChart>();
protected long lastResizeTime = 0;
protected JDialog dialog = null;
//delay in resizing chart, after component resize. For performance
protected int resizeDelayMS = 200;
public MultiChartDisplayPanel()
{
super();
//setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
addComponentListener(new ResizeListener());
}
public static void destroySingleton()
{
_singletonInstance = null;
}
/**
* Adds a new menu item to the PanelWithChart for saving of all charts
* @param newPanel
*/
public void addChartPanel(PanelWithChart newPanel)
{
synchronized (chartPanels)
{
chartPanels.add(newPanel);
}
resizeChartPanel(newPanel);
addChartPanelToGUI(newPanel);
newPanel.addSeparatorToPopupMenu();
newPanel.addItemToPopupMenu(createSaveAllMenuItem());
}
protected abstract void addChartPanelToGUI(PanelWithChart newPanel);
protected abstract void removeChartPanelFromGUI(PanelWithChart panel);
public void removeAllCharts()
{
for (PanelWithChart panel : chartPanels)
{
removeChartPanelFromGUI(panel);
}
synchronized(chartPanels)
{
chartPanels.removeAll(chartPanels);
}
}
public int getNumCharts()
{
return chartPanels.size();
}
public void removeChartPanel(PanelWithChart panel)
{
synchronized (chartPanels)
{
chartPanels.remove(panel);
}
removeChartPanelFromGUI(panel);
}
/**
* Creates a "save all charts" menu item
* @return
*/
protected JMenuItem createSaveAllMenuItem()
{
JMenuItem saveAllMenuItem = new JMenuItem("Save All Charts");
saveAllMenuItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
JFileChooser fc = new JFileChooser();
fc.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
int chooserStatus = fc.showOpenDialog(MultiChartDisplayPanel.this);
//if user didn't hit OK, ignore
if (chooserStatus != JFileChooser.APPROVE_OPTION)
return;
File outDir = fc.getSelectedFile();
try
{
saveAllChartsToFiles(outDir);
ApplicationContext.infoMessage("All charts saved to directory " + outDir.getAbsolutePath());
}
catch (IOException e)
{
ApplicationContext.errorMessage("Failed to save charts to directory " +
outDir.getAbsolutePath(),e);
}
}
}
);
return saveAllMenuItem;
}
/**
* Resize a chart panel appropriately
* @param pwc
*/
protected void resizeChartPanel(PanelWithChart pwc)
{
}
protected abstract void resizeChartInFocus();
protected class TabFocusListener implements ChangeListener
{
public void stateChanged(ChangeEvent e)
{
resizeChartInFocus();
}
}
/**
* Resize all chart panels when this panel is resized
*/
protected class ResizeListener implements ComponentListener
{
public void componentResized(ComponentEvent event)
{
resizeChartInFocus();
}
public void componentMoved(ComponentEvent event) {}
public void componentShown(ComponentEvent event) {}
public void componentHidden(ComponentEvent event) {}
}
public static MultiChartDisplayPanel getSingletonInstance()
{
if (_singletonInstance == null)
_singletonInstance = new TabbedMultiChartDisplayPanel();
return _singletonInstance;
}
/**
* Returns the old singleton instance, if any;
* @return
*/
public static MultiChartDisplayPanel createNewSingletonInstance()
{
MultiChartDisplayPanel result = _singletonInstance;
_singletonInstance = new TabbedMultiChartDisplayPanel();
return result;
}
public JDialog displayInDialog()
{
return displayInDialog("Charts", _hiddenMode);
}
public JDialog displayInDialog(String dialogTitle, boolean hidden)
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
dialog = new JDialog();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setTitle(dialogTitle);
dialog.setSize(new Dimension(getWidth() + 10, getHeight() + 25));
dialog.add(this);
if (!hidden)
dialog.setVisible(true);
return dialog;
}
public JDialog getDialog()
{
return dialog;
}
public static void addAndDisplayChartOnSingleton(PanelWithChart pwc)
{
if (_singletonInstance == null || getSingletonInstance().getDialog() == null || !getSingletonInstance().getDialog().isDisplayable())
{
getSingletonInstance().displayInDialog();
}
getSingletonInstance().addChartPanel(pwc);
}
/**
* Saves all charts to files in a given directory
* @param outputDir
* @throws IOException
*/
public void saveAllChartsToFiles(File outputDir)
throws IOException
{
if (!outputDir.isDirectory())
throw new IOException("saveAllChartsToFiles: File " + outputDir.getAbsolutePath() + " is not a directory");
if (!outputDir.exists() || !outputDir.canWrite())
throw new IOException("saveAllChartsToFiles: nonexistent or unwriteable directory " + outputDir.getAbsolutePath());
for (PanelWithChart pwc : getChartPanels())
pwc.saveChartToImageFile(new File(outputDir, createChartFileName(pwc.getName())));
}
/**
* vertical orientation
* @param charts
* @return
*/
public static BufferedImage createImageForAllCharts(List<PanelWithChart> charts)
{
int maxWidth = 0;
int totalHeight = 0;
List<Integer> heights = new ArrayList<Integer>();
for (PanelWithChart pwc : charts)
{
int currentWidth = pwc.getWidth();
if (currentWidth == 0)
currentWidth = PanelWithChart.DEFAULT_WIDTH_FOR_IMAGE_FILE;
if (pwc.getWidth() > maxWidth)
maxWidth = pwc.getWidth();
int currentHeight = pwc.getHeight();
if (currentHeight == 0)
currentHeight = PanelWithChart.DEFAULT_HEIGHT_FOR_IMAGE_FILE;
heights.add(currentHeight);
totalHeight += currentHeight;
}
BufferedImage bigImage = new BufferedImage(maxWidth, totalHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bigImage.createGraphics();
int currentHeightCum = 0;
for (int i=0; i<charts.size(); i++)
{
PanelWithChart pwc = charts.get(i);
Image thisChartImage = pwc.createImage();
g.drawImage(thisChartImage, 0, currentHeightCum, null);
currentHeightCum += heights.get(i);
}
g.dispose();
return bigImage;
}
/**
* Dump all charts to one file, vertically
* @param outFile
*/
public void saveAllChartsToFile(File outFile)
throws IOException
{
BufferedImage bigImage = createImageForAllCharts(getChartPanels());
ImageIO.write(bigImage,"png",outFile);
}
/**
* Create a safe name for a chart
* @param chartTitle
* @return
*/
protected String createChartFileName(String chartTitle)
{
String result = chartTitle + ".png";
try
{
result = URLEncoder.encode(chartTitle, "US-ASCII") + ".png";
}
catch (UnsupportedEncodingException e)
{
ApplicationContext.infoMessage("Error encoding filename for chart named " + chartTitle);
}
return result;
}
public List<PanelWithChart> getChartPanels()
{
return chartPanels;
}
public static boolean isHiddenMode()
{
return _hiddenMode;
}
public static void setHiddenMode(boolean _hiddenMode)
{
MultiChartDisplayPanel._hiddenMode = _hiddenMode;
if (_singletonInstance != null && _singletonInstance.getDialog() != null)
{
_singletonInstance.getDialog().setVisible(!_hiddenMode);
}
}
public int getResizeDelayMS()
{
return resizeDelayMS;
}
public void setResizeDelayMS(int resizeDelayMS)
{
this.resizeDelayMS = resizeDelayMS;
}
}