/*
* MapperFrameOld.java
*
* Copyright (c) 2002-2015 Alexei Drummond, Andrew Rambaut and Marc Suchard
*
* This file is part of BEAST.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership and licensing.
*
* BEAST is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* BEAST 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with BEAST; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301 USA
*/
package dr.app.mapper.application;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.pdf.DefaultFontMapper;
import com.lowagie.text.pdf.PdfContentByte;
import com.lowagie.text.pdf.PdfTemplate;
import com.lowagie.text.pdf.PdfWriter;
import dr.app.gui.FileDrop;
import dr.app.gui.table.TableEditorStopper;
import dr.app.gui.util.LongTask;
import dr.app.mapper.application.mapper.Layer;
import dr.app.mapper.application.mapper.MapperPanel;
import dr.inference.trace.*;
import jam.framework.DocumentFrame;
import jam.panels.ActionPanel;
import jam.table.TableRenderer;
import javax.swing.*;
import javax.swing.border.Border;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.plaf.BorderUIResource;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.Rectangle2D;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
/**
* @author Andrew Rambaut
* @version $Id$
*/
public class MapperFrameOld extends DocumentFrame {
private final String[] columnToolTips = {"", "", ""};
private MapperPanel mapperPanel = null;
private JTable traceTable = null;
private TraceTableModel traceTableModel = null;
private JSplitPane splitPane1 = null;
private JPanel topPanel = null;
private JTable layerTable = null;
private LayerTableModel layerTableModel = null;
private JScrollPane scrollPane1;
private JLabel progressLabel;
private JProgressBar progressBar;
private final List<LogFileTraces> traceLists = new ArrayList<LogFileTraces>();
private final List<Layer> layers = new ArrayList<Layer>();
String message = "";
private int dividerLocation = -1;
public MapperFrameOld(String title) {
super();
setTitle(title);
getOpenAction().setEnabled(false);
getSaveAction().setEnabled(false);
getSaveAsAction().setEnabled(false);
getCutAction().setEnabled(false);
getCopyAction().setEnabled(false);
getPasteAction().setEnabled(false);
getDeleteAction().setEnabled(false);
getSelectAllAction().setEnabled(false);
getFindAction().setEnabled(false);
getZoomWindowAction().setEnabled(false);
AbstractAction importAction = new AbstractAction("Import Trace File...") {
public void actionPerformed(ActionEvent ae) {
doImport();
}
};
setImportAction(importAction);
setExportAction(exportDataAction);
setAnalysesEnabled(false);
}
public void initializeComponents() {
setSize(new java.awt.Dimension(1200, 800));
mapperPanel = new MapperPanel(this);
mapperPanel.setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(12, 6, 12, 12)));
traceTableModel = new TraceTableModel();
traceTable = new JTable(traceTableModel);
TableRenderer renderer = new TableRenderer(SwingConstants.LEFT, new Insets(0, 4, 0, 4));
traceTable.getColumnModel().getColumn(0).setCellRenderer(renderer);
traceTable.getColumnModel().getColumn(1).setPreferredWidth(50);
traceTable.getColumnModel().getColumn(1).setCellRenderer(renderer);
traceTable.getColumnModel().getColumn(2).setPreferredWidth(50);
traceTable.getColumnModel().getColumn(2).setCellRenderer(renderer);
traceTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
traceTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent evt) {
traceTableSelectionChanged();
}
});
scrollPane1 = new JScrollPane(traceTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
ActionPanel actionPanel1 = new ActionPanel(false);
actionPanel1.setAddAction(getImportAction());
actionPanel1.setRemoveAction(getRemoveTraceAction());
getRemoveTraceAction().setEnabled(false);
JPanel controlPanel1 = new JPanel(new FlowLayout(FlowLayout.LEFT));
controlPanel1.add(actionPanel1);
topPanel = new JPanel(new BorderLayout(0, 0));
topPanel.setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(0, 0, 6, 0)));
topPanel.add(new JLabel("Trace Files:"), BorderLayout.NORTH);
topPanel.add(scrollPane1, BorderLayout.CENTER);
topPanel.add(controlPanel1, BorderLayout.SOUTH);
layerTableModel = new LayerTableModel();
layerTable = new JTable(layerTableModel) {
//Implement table header tool tips.
protected JTableHeader createDefaultTableHeader() {
return new JTableHeader(columnModel) {
public String getToolTipText(MouseEvent e) {
java.awt.Point p = e.getPoint();
int index = columnModel.getColumnIndexAtX(p.x);
int realIndex = columnModel.getColumn(index).getModelIndex();
return columnToolTips[realIndex];
}
};
}
};
layerTable.getColumnModel().getColumn(0).setPreferredWidth(150);
layerTable.getColumnModel().getColumn(0).setCellRenderer(renderer);
layerTable.getColumnModel().getColumn(1).setPreferredWidth(70);
layerTable.getColumnModel().getColumn(1).setCellRenderer(renderer);
// layerTable.getColumnModel().getColumn(2).setPreferredWidth(70);
// layerTable.getColumnModel().getColumn(2).setCellRenderer(renderer);
// ComboBoxRenderer comboBoxRenderer = new ComboBoxRenderer(TraceFactory.TraceType.values());
// comboBoxRenderer.putClientProperty("JComboBox.isTableCellEditor", Boolean.TRUE);
// layerTable.getColumnModel().getColumn(3).setPreferredWidth(20);
// layerTable.getColumnModel().getColumn(3).setCellRenderer(renderer);
layerTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
layerTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
public void valueChanged(ListSelectionEvent evt) {
layerTableSelectionChanged();
}
});
TableEditorStopper.ensureEditingStopWhenTableLosesFocus(layerTable);
JScrollPane scrollPane2 = new JScrollPane(layerTable, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
JPanel bottomPanel = new JPanel(new BorderLayout(0, 0));
bottomPanel.add(new JLabel("Layers:"), BorderLayout.NORTH);
bottomPanel.add(scrollPane2, BorderLayout.CENTER);
JPanel leftPanel = new JPanel(new BorderLayout(0, 0));
leftPanel.setPreferredSize(new Dimension(400, 300));
splitPane1 = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, topPanel, bottomPanel);
splitPane1.setBorder(null);
JPanel progressPanel = new JPanel(new BorderLayout(0, 0));
progressLabel = new JLabel("");
progressBar = new JProgressBar();
progressPanel.add(progressLabel, BorderLayout.NORTH);
progressPanel.add(progressBar, BorderLayout.CENTER);
progressPanel.setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(6, 0, 0, 0)));
leftPanel.add(splitPane1, BorderLayout.CENTER);
leftPanel.add(progressPanel, BorderLayout.SOUTH);
leftPanel.setBorder(new BorderUIResource.EmptyBorderUIResource(new java.awt.Insets(12, 12, 12, 6)));
JSplitPane splitPane2 = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, leftPanel, mapperPanel);
splitPane2.setBorder(null);
splitPane2.setDividerLocation(350);
Color focusColor = UIManager.getColor("Focus.color");
Border focusBorder = BorderFactory.createMatteBorder(2, 2, 2, 2, focusColor);
splitPane1.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
new FileDrop(null, splitPane1, focusBorder, new FileDrop.Listener() {
public void filesDropped(java.io.File[] files) {
importFiles(files);
} // end filesDropped
}); // end FileDrop.Listener
getContentPane().setLayout(new java.awt.BorderLayout(0, 0));
getContentPane().add(splitPane2, BorderLayout.CENTER);
splitPane1.setDividerLocation(2000);
}
public void setVisible(boolean b) {
super.setVisible(b);
setupDividerLocation();
}
private void setupDividerLocation() {
if (dividerLocation == -1 || dividerLocation == splitPane1.getDividerLocation()) {
int h0 = topPanel.getHeight();
int h1 = scrollPane1.getViewport().getHeight();
int h2 = traceTable.getPreferredSize().height;
dividerLocation = h2 + h0 - h1;
// int h0 = topPanel.getHeight() - scrollPane1.getViewport().getHeight();
// dividerLocation = traceTable.getPreferredSize().height + h0;
if (dividerLocation > 400) dividerLocation = 400;
splitPane1.setDividerLocation(dividerLocation);
}
}
public void setAnalysesEnabled(boolean enabled) {
getExportAction().setEnabled(enabled);
getExportDataAction().setEnabled(enabled);
getExportPDFAction().setEnabled(enabled);
getCopyAction().setEnabled(true);
}
public void addTraceList(LogFileTraces traceList) {
int[] selRows = traceTable.getSelectedRows();
traceLists.add(traceList);
setAnalysesEnabled(true);
traceTableModel.fireTableDataChanged();
int newRow = traceLists.size() - 1;
traceTable.getSelectionModel().setSelectionInterval(newRow, newRow);
if (selRows.length > 1) {
for (int row : selRows) {
if (row == traceLists.size() - 1) {
row = traceLists.size();
}
traceTable.getSelectionModel().addSelectionInterval(row, row);
}
}
setupDividerLocation();
}
private void removeTraceList() {
int[] selRows = traceTable.getSelectedRows();
LogFileTraces[] tls = new LogFileTraces[selRows.length];
int i = 0;
for (int row : selRows) {
tls[i] = traceLists.get(row);
i++;
}
for (LogFileTraces tl : tls) {
traceLists.remove(tl);
}
traceTableModel.fireTableDataChanged();
layerTableModel.fireTableDataChanged();
if (traceLists.size() == 0) {
getRemoveTraceAction().setEnabled(false);
setAnalysesEnabled(false);
layerTableModel.fireTableDataChanged();
}
if (traceLists.size() > 0) {
int row = selRows[0];
if (row >= traceLists.size()) {
row = traceLists.size() - 1;
}
traceTable.getSelectionModel().addSelectionInterval(row, row);
}
setupDividerLocation();
}
public void setBurnIn(int index, int burnIn) {
LogFileTraces trace = traceLists.get(index);
trace.setBurnIn(burnIn);
analyseTraceList(trace);
updateTraceTables();
}
public void updateTraceTables() {
int[] selectedTraces = traceTable.getSelectedRows();
int[] selectedStatistics = layerTable.getSelectedRows();
traceTableModel.fireTableDataChanged();
layerTableModel.fireTableDataChanged();
traceTable.getSelectionModel().clearSelection();
for (int row : selectedTraces) {
traceTable.getSelectionModel().addSelectionInterval(row, row);
}
layerTable.getSelectionModel().clearSelection();
for (int row : selectedStatistics) {
layerTable.getSelectionModel().addSelectionInterval(row, row);
}
}
public void traceTableSelectionChanged() {
int[] selRows = traceTable.getSelectedRows();
if (selRows.length == 0) {
getRemoveTraceAction().setEnabled(false);
setAnalysesEnabled(false);
return;
}
setAnalysesEnabled(true);
getRemoveTraceAction().setEnabled(true);
int[] rows = layerTable.getSelectedRows();
layerTableModel.fireTableDataChanged();
if (rows.length > 0) {
for (int row : rows) {
layerTable.getSelectionModel().addSelectionInterval(row, row);
}
} else {
layerTable.getSelectionModel().setSelectionInterval(0, 0);
}
}
public void layerTableSelectionChanged() {
}
public void analyseTraceList(TraceList job) {
if (analyseTask == null) {
analyseTask = new AnalyseTraceTask();
javax.swing.Timer timer = new javax.swing.Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent evt) {
progressBar.setMaximum(analyseTask.getLengthOfTask());
progressBar.setValue(analyseTask.getCurrent());
}
});
analyseTask.go();
timer.start();
}
analyseTask.add(job);
}
AnalyseTraceTask analyseTask = null;
class AnalyseTraceTask extends LongTask {
class AnalysisStack<T> {
private final java.util.List<T> jobs = new ArrayList<T>();
public synchronized void add(T job) {
jobs.add(job);
}
public synchronized int getCount() {
return jobs.size();
}
public synchronized T get(int index) {
return jobs.get(index);
}
public synchronized void remove(int index) {
jobs.remove(index);
}
}
private final AnalysisStack<TraceList> analysisStack = new AnalysisStack<TraceList>();
public AnalyseTraceTask() {
}
public void add(TraceList job) {
analysisStack.add(job);
current = 0;
}
public int getCurrent() {
return current;
}
public int getLengthOfTask() {
int count = 0;
for (int i = 0; i < analysisStack.getCount(); i++) {
count += analysisStack.get(i).getTraceCount();
}
return count;
}
public void stop() {
}
public boolean done() {
return false;
}
public String getDescription() {
return "Analysing Trace File...";
}
public String getMessage() {
return null;
}
public Object doWork() {
current = 0;
boolean textCleared = true;
do {
if (analysisStack.getCount() > 0) {
Object job = analysisStack.get(0);
TraceList tl = (TraceList) job;
try {
for (int i = 0; i < tl.getTraceCount(); i++) {
progressLabel.setText("Analysing " + tl.getName() + ":");
textCleared = false;
tl.analyseTrace(i);
repaint();
current += 1;
}
} catch (final Exception ex) {
// do nothing. An exception is sometimes fired when burnin is changed whilst in the
// middle of an analysis. This doesn't seem to matter as the analysis is restarted.
ex.printStackTrace();
// EventQueue.invokeLater (
// new Runnable () {
// public void run () {
// JOptionPane.showMessageDialog(TracerFrame.this, "Fatal exception: " + ex.getMessage(),
// "Error reading file",
// JOptionPane.ERROR_MESSAGE);
// }
// });
}
analysisStack.remove(0);
} else {
if (!textCleared) {
progressLabel.setText("");
textCleared = true;
}
try {
Thread.sleep(500);
} catch (InterruptedException ie) {
// do nothing
}
}
} while (true);
}
//private int lengthOfTask = 0;
private int current = 0;
//private String message;
}
public final void doExportData() {
FileDialog dialog = new FileDialog(this,
"Export Data...",
FileDialog.SAVE);
dialog.setVisible(true);
if (dialog.getFile() != null) {
File file = new File(dialog.getDirectory(), dialog.getFile());
try {
FileWriter writer = new FileWriter(file);
writer.write(mapperPanel.getExportText());
writer.close();
} catch (IOException ioe) {
JOptionPane.showMessageDialog(this, "Unable to write file: " + ioe,
"Unable to write file",
JOptionPane.ERROR_MESSAGE);
}
}
}
public final void doExportPDF() {
FileDialog dialog = new FileDialog(this,
"Export PDF Image...",
FileDialog.SAVE);
dialog.setVisible(true);
if (dialog.getFile() != null) {
File file = new File(dialog.getDirectory(), dialog.getFile());
Rectangle2D bounds = mapperPanel.getExportableComponent().getBounds();
Document document = new Document(new com.lowagie.text.Rectangle((float) bounds.getWidth(), (float) bounds.getHeight()));
try {
// step 2
PdfWriter writer;
writer = PdfWriter.getInstance(document, new FileOutputStream(file));
// step 3
document.open();
// step 4
PdfContentByte cb = writer.getDirectContent();
PdfTemplate tp = cb.createTemplate((float) bounds.getWidth(), (float) bounds.getHeight());
Graphics2D g2d = tp.createGraphics((float) bounds.getWidth(), (float) bounds.getHeight(), new DefaultFontMapper());
mapperPanel.getExportableComponent().print(g2d);
g2d.dispose();
cb.addTemplate(tp, 0, 0);
}
catch (DocumentException de) {
JOptionPane.showMessageDialog(this, "Error writing PDF file: " + de,
"Export PDF Error",
JOptionPane.ERROR_MESSAGE);
}
catch (FileNotFoundException e) {
JOptionPane.showMessageDialog(this, "Error writing PDF file: " + e,
"Export PDF Error",
JOptionPane.ERROR_MESSAGE);
}
document.close();
}
}
public final void doImport() {
final JFileChooser chooser = new JFileChooser(openDefaultDirectory);
chooser.setMultiSelectionEnabled(true);
FileNameExtensionFilter filter = new FileNameExtensionFilter("BEAST log (*.log) Files", "log", "txt");
chooser.setFileFilter(filter);
final int returnVal = chooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
File[] files = chooser.getSelectedFiles();
importFiles(files);
}
}
private void importFiles(File[] files) {
LogFileTraces[] traces = new LogFileTraces[files.length];
for (int i = 0; i < files.length; i++) {
traces[i] = new LogFileTraces(files[i].getName(), files[i]);
}
processTraces(traces);
}
private File openDefaultDirectory = null;
private void setDefaultDir(File file) {
final String s = file.getAbsolutePath();
String p = s.substring(0, s.length() - file.getName().length());
openDefaultDirectory = new File(p);
if (!openDefaultDirectory.isDirectory()) {
openDefaultDirectory = null;
}
}
protected void processTraces(final LogFileTraces[] tracesArray) {
final JFrame frame = this;
// set default dir to directory of last file
setDefaultDir(tracesArray[tracesArray.length - 1].getFile());
if (tracesArray.length == 1) {
try {
final LogFileTraces traces = tracesArray[0];
final String fileName = traces.getName();
final ProgressMonitorInputStream in = new ProgressMonitorInputStream(
this,
"Reading " + fileName,
new FileInputStream(traces.getFile()));
in.getProgressMonitor().setMillisToDecideToPopup(0);
in.getProgressMonitor().setMillisToPopup(0);
// final Reader reader = new InputStreamReader(in);
Thread readThread = new Thread() {
public void run() {
try {
traces.loadTraces(in);
EventQueue.invokeLater(
new Runnable() {
public void run() {
analyseTraceList(traces);
addTraceList(traces);
}
});
} catch (final TraceException te) {
EventQueue.invokeLater(
new Runnable() {
public void run() {
JOptionPane.showMessageDialog(frame, "Problem with trace file: " + te.getMessage(),
"Problem with tree file",
JOptionPane.ERROR_MESSAGE);
}
});
} catch (final InterruptedIOException iioex) {
// The cancel dialog button was pressed - do nothing
} catch (final IOException ioex) {
EventQueue.invokeLater(
new Runnable() {
public void run() {
JOptionPane.showMessageDialog(frame, "File I/O Error: " + ioex.getMessage(),
"File I/O Error",
JOptionPane.ERROR_MESSAGE);
}
});
// } catch (final Exception ex) {
// EventQueue.invokeLater (
// new Runnable () {
// public void run () {
// JOptionPane.showMessageDialog(frame, "Fatal exception: " + ex.getMessage(),
// "Error reading file",
// JOptionPane.ERROR_MESSAGE);
// }
// });
}
}
};
readThread.start();
} catch (FileNotFoundException fnfe) {
JOptionPane.showMessageDialog(this, "Unable to open file: File not found",
"Unable to open file",
JOptionPane.ERROR_MESSAGE);
} catch (IOException ioex) {
JOptionPane.showMessageDialog(this, "File I/O Error: " + ioex,
"File I/O Error",
JOptionPane.ERROR_MESSAGE);
} catch (Exception ex) {
JOptionPane.showMessageDialog(this, "Fatal exception: " + ex,
"Error reading file",
JOptionPane.ERROR_MESSAGE);
}
} else {
Thread readThread = new Thread() {
public void run() {
try {
for (final LogFileTraces traces : tracesArray) {
// final Reader reader = new FileReader(traces.getFile());
traces.loadTraces();
EventQueue.invokeLater(
new Runnable() {
public void run() {
analyseTraceList(traces);
addTraceList(traces);
}
});
}
} catch (final TraceException te) {
EventQueue.invokeLater(
new Runnable() {
public void run() {
JOptionPane.showMessageDialog(frame, "Problem with trace file: " + te.getMessage(),
"Problem with tree file",
JOptionPane.ERROR_MESSAGE);
}
});
} catch (final InterruptedIOException iioex) {
// The cancel dialog button was pressed - do nothing
} catch (final IOException ioex) {
EventQueue.invokeLater(
new Runnable() {
public void run() {
JOptionPane.showMessageDialog(frame, "File I/O Error: " + ioex.getMessage(),
"File I/O Error",
JOptionPane.ERROR_MESSAGE);
}
});
// } catch (final Exception ex) {
// EventQueue.invokeLater (
// new Runnable () {
// public void run () {
// JOptionPane.showMessageDialog(frame, "Fatal exception: " + ex.getMessage(),
// "Error reading file",
// JOptionPane.ERROR_MESSAGE);
// }
// });
}
}
};
readThread.start();
}
}
protected boolean readFromFile(File file) throws IOException {
throw new RuntimeException("Cannot read file - use import instead");
}
protected boolean writeToFile(File file) {
throw new RuntimeException("Cannot write file - this is a read-only application");
}
public void doCopy() {
mapperPanel.doCopy();
}
public JComponent getExportableComponent() {
return mapperPanel.getExportableComponent();
}
class TraceTableModel extends AbstractTableModel {
final String[] columnNames = {"Trace File", "States", "Burn-In"};
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
int n = traceLists.size();
if (n == 0) n++;
return n;
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
TraceList traceList;
if (traceLists.size() == 0) {
switch (col) {
case 0:
return "No files loaded";
case 1:
return "";
case 2:
return "";
}
} else {
traceList = traceLists.get(row);
switch (col) {
case 0:
return traceList.getName();
case 1:
return traceList.getMaxState();
case 2:
return traceList.getBurnIn();
}
}
return null;
}
public void setValueAt(Object value, int row, int col) {
if (col == 2) {
setBurnIn(row, (Integer) value);
}
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
public boolean isCellEditable(int row, int col) {
return col == 2 && row < traceLists.size();
}
}
class LayerTableModel extends AbstractTableModel {
final String[] columnNames = {"Layer", "Type"};
public int getColumnCount() {
return columnNames.length;
}
public int getRowCount() {
return layers.size();
}
public String getColumnName(int col) {
return columnNames[col];
}
public Object getValueAt(int row, int col) {
Layer layer = layers.get(row);
if (col == 0) return layer.getName();
if (col == 1) return layer.getType().toString();
return "";
}
public boolean isCellEditable(int row, int col) {
return true;
}
public Class getColumnClass(int c) {
if (getRowCount() == 0) {
return Object.class;
}
return getValueAt(0, c).getClass();
}
}
public Action getExportDataAction() {
return exportDataAction;
}
public Action getExportPDFAction() {
return exportPDFAction;
}
public Action getRemoveTraceAction() {
return removeTraceAction;
}
private final AbstractAction removeTraceAction = new AbstractAction() {
public void actionPerformed(ActionEvent ae) {
removeTraceList();
}
};
private final AbstractAction exportDataAction = new AbstractAction("Export Data...") {
public void actionPerformed(ActionEvent ae) {
doExportData();
}
};
private final AbstractAction exportPDFAction = new AbstractAction("Export PDF...") {
public void actionPerformed(ActionEvent ae) {
doExportPDF();
}
};
}