package ij.measure;
import ij.plugin.filter.Analyzer;
import ij.plugin.frame.Recorder;
import ij.plugin.*;
import ij.*;
import ij.gui.*;
import ij.text.*;
import java.awt.*;
import java.awt.event.*;
public class ResultsTableMacros implements Runnable, DialogListener, ActionListener, KeyListener {
private static String NAME = "TableMacro.ijm";
private String defaultMacro = "Sin=sin(row*0.1);\nCos=cos(row*0.1);\nSqr=Sin*Sin+Cos*Cos;";
private GenericDialog gd;
private ResultsTable rt, rtBackup;
private Button runButton, resetButton, openButton, saveButton;
private String title;
private int runCount;
private TextArea ta;
public ResultsTableMacros(ResultsTable rt) {
this.rt = rt;
title = rt!=null?rt.getTitle():null;
Thread thread = new Thread(this, "ResultTableMacros");
thread.start();
}
private void showDialog() {
if (rt==null)
rt = Analyzer.getResultsTable();
if (rt==null || rt.size()==0) {
IJ.error("Results Table required");
return;
}
String[] temp = rt.getHeadingsAsVariableNames();
String[] variableNames = new String[temp.length+2];
variableNames[0] = "Insert...";
variableNames[1] = "row";
for (int i=2; i<variableNames.length; i++)
variableNames[i] = temp[i-2];
String dialogTitle = "Apply Macro to "+(title!=null?"\""+title+"\"":"Table");
Frame parent = title!=null?WindowManager.getFrame(title):null;
if (parent!=null)
gd = new GenericDialog(dialogTitle, parent);
else
gd = new GenericDialog(dialogTitle);
gd.setInsets(5, 5, 0);
gd.addTextAreas(getMacro(), null, 12, 48);
ta = gd.getTextArea1();
ta.addKeyListener(this);
Panel panel = new Panel();
if (IJ.isMacOSX())
panel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
runButton = new Button("Run");
runButton.addActionListener(this);
panel.add(runButton);
resetButton = new Button("Reset");
resetButton.addActionListener(this);
panel.add(resetButton);
openButton = new Button("Open");
openButton.addActionListener(this);
panel.add(openButton);
saveButton = new Button("Save");
saveButton.addActionListener(this);
panel.add(saveButton);
gd.addPanel(panel);
gd.addToSameRow();
gd.addChoice("", variableNames, variableNames[0]);
gd.addHelp("<html><body><h1>Macro Equations for Results Tables</h1><ul>"+
"<li>The macro, or a selection, is applied to each row of the table."+
"<li>A new variable starting with an Uppercase character creates<br>a new column."+
"<li>A new variable starting with a lowercase character is temporary."+
"<li>The variable <b>row</b> (row index) is pre-defined.\n"+
"<li>String operations are supported for the 'Label' column only (if<br>enabled"+
"with Analyze>Set Measurements>Display Label)."+
"<li>Click \"<b>Run</b>\", or press "+(IJ.isMacOSX()?"cmd":"ctrl") + "-r, to apply the macro code to the table."+
"<li>Select a line and press "+(IJ.isMacOSX()?"cmd":"ctrl") + "-r to apply a line of macro code."+
"<li>Click \"<b>Reset</b>\" to revert to the original version of the table."+
"<li>The code is saved at <b>macros/TableMacro.ijm</b>, and the<br>\"Apply Macro\" command is recorded, when you click \"<b>OK</b>\"."+
"<li>All <b>Table.</b> macro functions (such as Table.size) refer to the<br>current (frontmost) table unless the table name is given."+
"</ul></body></html>");
gd.addDialogListener(this);
gd.showDialog();
if (gd.wasCanceled()) { rt = rtBackup;
updateDisplay();
return;
}
if (runCount==0)
applyMacro();
if (Recorder.record) {
String macro = getMacroCode();
macro = macro.replaceAll("\n", " ");
if (Recorder.scriptMode()) {
Recorder.recordCall("title = \""+title+"\";");
Recorder.recordCall("frame = WindowManager.getFrame(title);");
Recorder.recordCall("rt = frame.getResultsTable();");
Recorder.recordCall("rt.applyMacro(\""+macro+"\");");
Recorder.recordCall("rt.show(title);");
} else {
if (title.equals("Results"))
Recorder.record("Table.applyMacro", macro);
else
Recorder.record("Table.applyMacro", macro, title);
}
}
IJ.saveString(ta.getText(), IJ.getDir("macros")+NAME);
}
private void applyMacro() {
String code = getMacroCode();
rt.applyMacro(code);
updateDisplay();
runCount++;
}
private String getMacroCode() {
int start = ta.getSelectionStart();
int end = ta.getSelectionEnd();
return start==end?ta.getText():ta.getSelectedText();
}
public boolean dialogItemChanged(GenericDialog gd, AWTEvent e) {
final String variableName = gd.getNextChoice();
if (e!=null && (e.getSource() instanceof Choice) && !variableName.equals("Insert...")) {
final int pos = ta.getCaretPosition();
((Choice)e.getSource()).select(0);
final TextArea textArea = ta;
new Thread(new Runnable() {
public void run() {
IJ.wait(100);
textArea.insert(variableName, pos);
textArea.setCaretPosition(pos+variableName.length());
textArea.requestFocus();
}}).start();
}
return true;
}
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source==runButton) {
applyMacro();
} else if (source==resetButton) {
rt = (ResultsTable)rtBackup.clone();
updateDisplay();
} else if (source==openButton) {
String macro = IJ.openAsString(null);
if (macro==null)
return;
if (macro.startsWith("Error: ")) {
IJ.error(macro);
return;
} else
ta.setText(macro);
} else if (source==saveButton) {
ta.selectAll();
String macro = ta.getText();
ta.select(0, 0);
IJ.saveString(macro, null);
}
}
public void keyPressed(KeyEvent e) {
int flags = e.getModifiers();
boolean control = (flags & KeyEvent.CTRL_MASK) != 0;
boolean meta = (flags & KeyEvent.META_MASK) != 0;
int keyCode = e.getKeyCode();
if (keyCode==KeyEvent.VK_R && (control||meta))
applyMacro();
if (keyCode==KeyEvent.VK_Z && (control||meta)) {
rt = (ResultsTable)rtBackup.clone();
updateDisplay();
}
}
private void updateDisplay() {
if (title!=null)
rt.show(title);
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
private String getMacro() {
String macro = IJ.openAsString(IJ.getDir("macros")+NAME);
if (macro==null || macro.startsWith("Error:"))
return defaultMacro;
else {
macro = macro.replaceAll("rowNumber", "row");
macro = macro.replaceAll("rowIndex", "row");
return macro;
}
}
public void run() {
rtBackup = (ResultsTable)rt.clone();
showDialog();
}
}