package cz.cuni.lf1.lge.ThunderSTORM.results;
import cz.cuni.lf1.lge.ThunderSTORM.ImportExportPlugIn;
import cz.cuni.lf1.lge.ThunderSTORM.UI.GUI;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.Molecule;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.MoleculeDescriptor;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.MoleculeDescriptor.Units;
import cz.cuni.lf1.lge.ThunderSTORM.estimators.PSF.PSFModel;
import ij.IJ;
import ij.WindowManager;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.io.File;
import java.io.IOException;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.List;
import java.util.Locale;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public class GenericTableWindow {
protected JFrame frame;
protected ColoredTable table;
protected final TripleStateTableModel model;
protected JScrollPane tableScrollPane;
public GenericTableWindow(String frameTitle) {
frame = new JFrame(frameTitle);
frame.setIconImage(IJ.getInstance().getIconImage());
frame.setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
//
model = new TripleStateTableModel();
table = new ColoredTable(model);
TableRowSorter<TripleStateTableModel> sorter = new TableRowSorter<TripleStateTableModel>(model);
table.setRowSorter(sorter);
//
table.getTableHeader().addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
tableHeaderMouseClicked(e);
}
});
table.setDropTarget(new TableDropTarget());
tableScrollPane = new JScrollPane(table);
tableScrollPane.setDropTarget(new TableDropTarget());
table.addMouseMotionListener(new MouseMotionListener() {
@Override
public void mouseDragged(MouseEvent e) {
tableMouseDragged(e);
}
@Override
public void mouseMoved(MouseEvent e) {
tableMouseMoved(e);
}
});
table.addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {
tableMouseClicked(e);
}
@Override
public void mousePressed(MouseEvent e) {
tableMousePressed(e);
}
@Override
public void mouseReleased(MouseEvent e) {
tableMouseReleased(e);
}
@Override
public void mouseEntered(MouseEvent e) {
tableMouseEntered(e);
}
@Override
public void mouseExited(MouseEvent e) {
tableMouseExited(e);
}
});
}
protected void packFrame() {
Container contentPane = frame.getContentPane();
contentPane.add(tableScrollPane);
frame.setContentPane(contentPane);
frame.pack();
}
public TripleStateTableModel getModel() {
return (TripleStateTableModel) table.getModel();
}
public JTable getView() {
return table;
}
public void show(String title) {
frame.setTitle(title);
show();
}
public void show() {
WindowManager.addWindow(frame); // ImageJ's own Window Manager
GUI.runOnUIThreadAndWait(new Runnable() {
@Override
public void run() {
frame.setVisible(true);
}
});
WindowManager.setWindow(frame); // ImageJ's own Window Manager
}
public void hide() {
frame.setVisible(false);
WindowManager.removeWindow(frame); // ImageJ's own Window Manager
}
public boolean isVisible() {
return frame.isVisible();
}
private class UnitsContextMenu implements ActionListener {
private int column;
public UnitsContextMenu(MouseEvent e, int column) {
this.column = column;
Units selected = model.getColumnUnits(column);
JPopupMenu popup = new JPopupMenu();
//
if(MoleculeDescriptor.LABEL_ID.equals(model.getColumnRealName(column))) {
JMenuItem item;
item = new JMenuItem("convert all to digital units");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.convertAllColumnsToDigitalUnits();
if(GenericTableWindow.this instanceof ResultsTableWindow) {
TableHandlerPlugin.recordChangeAllUnits(false);
}
}
});
popup.add(item);
item = new JMenuItem("convert all to analog units");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
model.convertAllColumnsToAnalogUnits();
if(GenericTableWindow.this instanceof ResultsTableWindow) {
TableHandlerPlugin.recordChangeAllUnits(true);
}
}
});
popup.add(item);
} else if(MoleculeDescriptor.Fitting.LABEL_UNCERTAINTY_XY.equals(model.getColumnRealName(column))) {
JMenuItem item = new JMenuItem("recalculate");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
model.calculateUncertaintyXY();
} catch (MoleculeDescriptor.Fitting.UncertaintyNotApplicableException ex) {
IJ.log("Cannot calculate lateral uncertainty: " + ex.getMessage());
} catch (NullPointerException ex) {
IJ.log("Measurement protocol wasn't set properly to calculate uncertainty!");
}
}
});
popup.add(item);
popup.add(new JSeparator());
} else if(MoleculeDescriptor.Fitting.LABEL_UNCERTAINTY_Z.equals(model.getColumnRealName(column))) {
JMenuItem item = new JMenuItem("recalculate");
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
model.calculateUncertaintyZ();
} catch (MoleculeDescriptor.Fitting.UncertaintyNotApplicableException ex) {
IJ.log("Cannot calculate axial uncertainty: " + ex.getMessage());
} catch (NullPointerException ex) {
IJ.log("Measurement protocol wasn't set properly to calculate uncertainty!");
}
}
});
popup.add(item);
popup.add(new JSeparator());
}
//
JRadioButtonMenuItem menuItem;
for(Units unit : Units.getCompatibleUnits(selected)) {
if(unit == Units.UNITLESS) {
continue;
}
if((PSFModel.Params.LABEL_Z.equals(model.getColumnRealName(column)))
|| (PSFModel.Params.LABEL_Z_REL.equals(model.getColumnRealName(column)))) {
if(unit == Units.PIXEL) {
continue; // z-position can't be converted to pixels
}
}
menuItem = new JRadioButtonMenuItem(unit.getLabel(), unit == selected);
menuItem.addActionListener(this);
popup.add(menuItem);
}
popup.show(e.getComponent(), e.getX(), e.getY());
}
@Override
public void actionPerformed(ActionEvent e) {
Units target = Units.fromString(e.getActionCommand());
if(model.getColumnUnits(column) == target) {
return; // nothing to do here
}
String colName = model.getColumnRealName(column);
if(PSFModel.Params.LABEL_X.equals(colName) || PSFModel.Params.LABEL_Y.equals(colName)) {
// ensure that X and Y are always in same units!
model.setColumnUnits(PSFModel.Params.LABEL_X, target);
model.setColumnUnits(PSFModel.Params.LABEL_Y, target);
} else {
model.setColumnUnits(column, target);
}
if(GenericTableWindow.this instanceof ResultsTableWindow) {
TableHandlerPlugin.recordChangeColumnUnits(colName, target);
}
}
}
private class TableDropTarget extends DropTarget {
@Override
public synchronized void drop(DropTargetDropEvent dtde) {
dtde.acceptDrop(DnDConstants.ACTION_REFERENCE);
Transferable t = dtde.getTransferable();
List fileList;
try {
fileList = (List) t.getTransferData(DataFlavor.javaFileListFlavor);
} catch(UnsupportedFlavorException ex) {
return;
} catch(IOException ex) {
return;
}
File f = (File) fileList.get(0);
dropFile(f);
}
}
protected void dropFile(File f) {
new ImportExportPlugIn(f.getAbsolutePath()).run(ImportExportPlugIn.IMPORT + IJResultsTable.IDENTIFIER);
}
protected void tableHeaderMouseClicked(MouseEvent e) {
if(SwingUtilities.isRightMouseButton(e)) {
new UnitsContextMenu(e, table.convertColumnIndexToModel(table.columnAtPoint(e.getPoint())));
}
}
protected void tableMouseDragged(MouseEvent e) {
//
}
protected void tableMouseMoved(MouseEvent e) {
//
}
protected void tableMouseClicked(MouseEvent e) {
//
}
protected void tableMousePressed(MouseEvent e) {
//
}
protected void tableMouseReleased(MouseEvent e) {
//
}
protected void tableMouseEntered(MouseEvent e) {
//
}
protected void tableMouseExited(MouseEvent e) {
//
}
// =============================================================
public class ColoredTable extends JTable {
public final Color LIGHT_ORANGE = new Color(255, 222, 200);
public final Color LIGHT_RED = new Color(255, 222, 222);
public final Color LIGHT_GREEN = new Color(222, 255, 222);
public ColoredTable(TableModel dm) {
super(dm);
}
@Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
final Component c = super.prepareRenderer(renderer, row, column);
if (column < 0 || column >= getColumnCount()) return c;
if (row < 0 || row >= getRowCount()) return c;
NumberFormat formatter = NumberFormat.getInstance(Locale.ENGLISH);
((DecimalFormat) formatter).setGroupingUsed(false);
((JLabel) c).setText(formatter.format(getValueAt(row, column)));
if(!isCellSelected(row, column)) {
c.setBackground(null);
Molecule mol = ((GenericTableModel) super.getModel()).getRow(convertRowIndexToModel(row));
switch(mol.getStatus()) {
case FALSE_POSITIVE:
c.setBackground(LIGHT_RED);
break;
case FALSE_NEGATIVE:
c.setBackground(LIGHT_ORANGE);
break;
case TRUE_POSITIVE:
c.setBackground(LIGHT_GREEN);
break;
default:
c.setBackground(Color.WHITE);
}
}
return c;
}
}
}