package de.danielsenff.imageflow.imagej;
import ij.CompositeImage;
import ij.IJ;
import ij.ImagePlus;
import ij.Macro;
import ij.Prefs;
import ij.WindowManager;
import ij.gui.DialogListener;
import ij.gui.GUI;
import ij.plugin.ScreenGrabber;
import ij.plugin.filter.PlugInFilterRunner;
import ij.plugin.frame.Recorder;
import ij.util.Tools;
import java.awt.AWTEvent;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.SystemColor;
import java.awt.TextArea;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.TextEvent;
import java.awt.event.TextListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.util.Hashtable;
import java.util.Vector;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollBar;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
* This class is a customizable modal dialog box. Here is an example
* GenericDialog with one string field and two numeric fields:
* <pre>
* public class Generic_Dialog_Example implements PlugIn {
* static String title="Example";
* static int width=512,height=512;
* public void run(String arg) {
* GenericDialog gd = new GenericDialog("New Image");
* gd.addStringField("Title: ", title);
* gd.addNumericField("Width: ", width, 0);
* gd.addNumericField("Height: ", height, 0);
* gd.showDialog();
* if (gd.wasCanceled()) return;
* title = gd.getNextString();
* width = (int)gd.getNextNumber();
* height = (int)gd.getNextNumber();
* IJ.newImage(title, "8-bit", width, height, 1);
* }
* }
* </pre>
* To work with macros, the first word of each component label must be
* unique. If this is not the case, add underscores, which will be converted
* to spaces when the dialog is displayed. For example, change the checkbox labels
* "Show Quality" and "Show Residue" to "Show_Quality" and "Show_Residue".
*/
public class GenericDialog extends JDialog implements ActionListener, TextListener,
FocusListener, ItemListener, KeyListener, AdjustmentListener, WindowListener {
public static final int MAX_SLIDERS = 25;
protected Vector numberField, stringField, checkbox, choice, slider;
protected JTextArea textArea1, textArea2;
protected Vector defaultValues,defaultText;
protected JComponent theLabel;
private JButton cancel, okay, no;
private String okLabel = " OK ";
private boolean wasCanceled, wasOKed;
private int y;
private int nfIndex, sfIndex, cbIndex, choiceIndex, textAreaIndex;
private final GridBagLayout grid;
private final GridBagConstraints c;
private boolean firstNumericField=true;
private boolean firstSlider=true;
private boolean invalidNumber;
private String errorMessage;
private Hashtable labels;
private final boolean macro;
private final String macroOptions;
private int topInset, leftInset, bottomInset;
private boolean customInsets;
private int[] sliderIndexes;
private JCheckBox previewCheckbox; // the "Preview" Checkbox, if any
private Vector dialogListeners; // the Objects to notify on user input
private PlugInFilterRunner pfr; // the PlugInFilterRunner for automatic preview
private String previewLabel = " Preview";
private final static String previewRunning = "wait...";
private boolean recorderOn; // whether recording is allowed
private boolean yesNoCancel;
private boolean hideCancelButton;
private boolean centerDialog = true;
/** Creates a new GenericDialog using the specified title and parent frame.
* @param title
* @param parent */
public GenericDialog(final String title, final JFrame parent) {
super(parent==null?new JFrame():parent, title, true);
final Container contentPane = getRootPane();
contentPane.setBackground(Color.black);
grid = new GridBagLayout();
c = new GridBagConstraints();
setLayout(grid);
macroOptions = Macro.getOptions();
macro = macroOptions!=null;
addKeyListener(this);
addWindowListener(this);
setResizable(false);
pack();
}
/** Adds a numeric field. The first word of the label must be
unique or command recording will not work.
* @param label the label
* @param defaultValue value to be initially displayed
* @param digits number of digits to right of decimal point
*/
public void addNumericField(final String label, final double defaultValue, final int digits) {
addNumericField(label, defaultValue, digits, 6, null);
}
/** Adds a numeric field. The first word of the label must be
unique or command recording will not work.
* @param label the label
* @param defaultValue value to be initially displayed
* @param digits number of digits to right of decimal point
* @param columns width of field in characters
* @param units a string displayed to the right of the field
*/
public void addNumericField(final String label, final double defaultValue, final int digits, int columns, final String units) {
String label2 = label;
if (label2.indexOf('_')!=-1)
label2 = label2.replace('_', ' ');
final JLabel theLabel = makeLabel(label2);
c.gridx = 0; c.gridy = y;
c.anchor = GridBagConstraints.EAST;
c.gridwidth = 1;
if (firstNumericField)
c.insets = getInsets(5, 0, 3, 0);
else
c.insets = getInsets(0, 0, 3, 0);
grid.setConstraints(theLabel, c);
add(theLabel);
if (numberField==null) {
numberField = new Vector(5);
defaultValues = new Vector(5);
defaultText = new Vector(5);
}
if (IJ.isWindows()) columns -= 2;
if (columns<1) columns = 1;
final JTextField tf = new JTextField(IJ.d2s(defaultValue, digits), columns);
if (IJ.isLinux()) tf.setBackground(Color.white);
tf.addActionListener(this);
// tf.addTextListener(this);
tf.addFocusListener(this);
tf.addKeyListener(this);
numberField.addElement(tf);
defaultValues.addElement(new Double(defaultValue));
defaultText.addElement(tf.getText());
c.gridx = 1; c.gridy = y;
c.anchor = GridBagConstraints.WEST;
tf.setEditable(true);
//if (firstNumericField) tf.selectAll();
firstNumericField = false;
if (units==null||units.equals("")) {
grid.setConstraints(tf, c);
add(tf);
} else {
final JPanel panel = new JPanel();
panel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
panel.add(tf);
panel.add(new JLabel(" "+units));
grid.setConstraints(panel, c);
add(panel);
}
if (Recorder.record || macro)
saveLabel(tf, label);
y++;
}
private JLabel makeLabel(String label) {
if (IJ.isMacintosh())
label += " ";
return new JLabel(label);
}
private void saveLabel(final JComponent component, String label) {
if (labels==null)
labels = new Hashtable();
if (label.length()>0) {
if (label.charAt(0)==' ')
label = label.trim();
labels.put(component, label);
}
}
/** Adds an 8 column text field.
* @param label the label
* @param defaultText the text initially displayed
*/
public void addStringField(final String label, final String defaultText) {
addStringField(label, defaultText, 8);
}
/** Adds a text field.
* @param label the label
* @param defaultText text initially displayed
* @param columns width of the text field
*/
public void addStringField(final String label, final String defaultText, final int columns) {
String label2 = label;
if (label2.indexOf('_')!=-1)
label2 = label2.replace('_', ' ');
final JLabel theLabel = makeLabel(label2);
c.gridx = 0; c.gridy = y;
c.anchor = GridBagConstraints.EAST;
c.gridwidth = 1;
final boolean custom = customInsets;
if (stringField==null) {
stringField = new Vector(4);
c.insets = getInsets(5, 0, 5, 0);
} else
c.insets = getInsets(0, 0, 5, 0);
grid.setConstraints(theLabel, c);
add(theLabel);
if (custom) {
if (stringField.size()==0)
c.insets = getInsets(5, 0, 5, 0);
else
c.insets = getInsets(0, 0, 5, 0);
}
final JTextField tf = new JTextField(defaultText, columns);
if (IJ.isLinux()) tf.setBackground(Color.white);
tf.addActionListener(this);
// tf.addTextListener(this);
tf.addFocusListener(this);
tf.addKeyListener(this);
c.gridx = 1; c.gridy = y;
c.anchor = GridBagConstraints.WEST;
grid.setConstraints(tf, c);
tf.setEditable(true);
add(tf);
stringField.addElement(tf);
if (Recorder.record || macro)
saveLabel(tf, label);
y++;
}
/** Adds a text field.
* @param label the label
* @param defaultText text initially displayed
* @param columns width of the text field
*/
public void addTextField(final String label, final String defaultText, final int columns) {
String label2 = label;
if (label2.indexOf('_')!=-1)
label2 = label2.replace('_', ' ');
final JLabel theLabel = makeLabel(label2);
c.gridx = 0; c.gridy = y;
c.anchor = GridBagConstraints.EAST;
c.gridwidth = 1;
final boolean custom = customInsets;
if (stringField==null) {
stringField = new Vector(4);
c.insets = getInsets(5, 0, 5, 0);
} else
c.insets = getInsets(0, 0, 5, 0);
grid.setConstraints(theLabel, c);
add(theLabel);
if (custom) {
if (stringField.size()==0)
c.insets = getInsets(5, 0, 5, 0);
else
c.insets = getInsets(0, 0, 5, 0);
}
final JTextArea tf = new JTextArea(defaultText);
tf.setPreferredSize(new Dimension(350, 150));
if (IJ.isLinux()) tf.setBackground(Color.white);
// tf.setEchoChar(echoChar);
// tf.addActionListener(this);
// tf.addTextListener(this);
tf.addFocusListener(this);
tf.addKeyListener(this);
c.gridx = 1; c.gridy = y;
c.anchor = GridBagConstraints.WEST;
grid.setConstraints(tf, c);
tf.setEditable(true);
add(tf);
stringField.addElement(tf);
if (Recorder.record || macro)
saveLabel(tf, label);
y++;
}
/** Adds a checkbox.
* @param label the label
* @param defaultValue the initial state
*/
public void addCheckbox(final String label, final boolean defaultValue) {
addCheckbox(label, defaultValue, false);
}
/** Adds a checkbox; does not make it recordable if isPreview is true.
* With isPreview true, the checkbox can be referred to as previewCheckbox
* from hereon.
*/
private void addCheckbox(final String label, final boolean defaultValue, final boolean isPreview) {
String label2 = label;
if (label2.indexOf('_')!=-1)
label2 = label2.replace('_', ' ');
if (checkbox==null) {
checkbox = new Vector(4);
c.insets = getInsets(15, 20, 0, 0);
} else
c.insets = getInsets(0, 20, 0, 0);
c.gridx = 0; c.gridy = y;
c.gridwidth = 2;
c.anchor = GridBagConstraints.WEST;
final JCheckBox cb = new JCheckBox(label2);
grid.setConstraints(cb, c);
cb.setSelected(defaultValue);
cb.addItemListener(this);
cb.addKeyListener(this);
add(cb);
checkbox.addElement(cb);
if (!isPreview &&(Recorder.record || macro)) //preview checkbox is not recordable
saveLabel(cb, label);
if (isPreview) previewCheckbox = cb;
y++;
}
/** Adds a checkbox labelled "Preview" for "automatic" preview.
* The reference to this checkbox can be retrieved by getPreviewCheckbox()
* and it provides the additional method previewRunning for optical
* feedback while preview is prepared.
* PlugInFilters can have their "run" method automatically called for
* preview under the following conditions:
* - the PlugInFilter must pass a reference to itself (i.e., "this") as an
* argument to the AddPreviewCheckbox
* - it must implement the DialogListener interface and set the filter
* parameters in the dialogItemChanged method.
* - it must have DIALOG and PREVIEW set in its flags.
* A previewCheckbox is always off when the filter is started and does not get
* recorded by the Macro Recorder.
*
* @param pfr A reference to the PlugInFilterRunner calling the PlugInFilter
* if automatic preview is desired, null otherwise.
*/
public void addPreviewCheckbox(final PlugInFilterRunner pfr) {
if (previewCheckbox != null)
return;
final ImagePlus imp = WindowManager.getCurrentImage();
if (imp!=null && imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE)
return;
this.pfr = pfr;
addCheckbox(previewLabel, false, true);
}
/** Add the preview checkbox with user-defined label; for details see the
* addPreviewCheckbox method with standard "Preview" label.
* Adds the checkbox when the current image is a CompositeImage
* in "Composite" mode, unlike the one argument version.
* Note that a GenericDialog can have only one PreviewCheckbox.
*/
public void addPreviewCheckbox(final PlugInFilterRunner pfr, final String label) {
if (previewCheckbox!=null)
return;
//ImagePlus imp = WindowManager.getCurrentImage();
//if (imp!=null && imp.isComposite() && ((CompositeImage)imp).getMode()==CompositeImage.COMPOSITE)
// return;
previewLabel = label;
this.pfr = pfr;
addCheckbox(previewLabel, false, true);
}
/** Adds a group of checkboxs using a grid layout.
* @param rows the number of rows
* @param columns the number of columns
* @param labels the labels
* @param defaultValues the initial states
*/
public void addCheckboxGroup(final int rows, final int columns, final String[] labels, final boolean[] defaultValues) {
final JPanel panel = new JPanel();
panel.setLayout(new GridLayout(rows,columns, 5, 0));
final int startCBIndex = cbIndex;
int i1 = 0;
final int[] index = new int[labels.length];
if (checkbox==null)
checkbox = new Vector(12);
final boolean addListeners = labels.length<=4;
for (int row=0; row<rows; row++) {
for (int col=0; col<columns; col++) {
final int i2 = col*rows+row;
if (i2>=labels.length) break;
index[i1] = i2;
String label = labels[i1];
if (label.indexOf('_')!=-1)
label = label.replace('_', ' ');
final JCheckBox cb = new JCheckBox(label);
checkbox.addElement(cb);
cb.setSelected(defaultValues[i1]);
if (addListeners) cb.addItemListener(this);
if (Recorder.record || macro)
saveLabel(cb, labels[i1]);
panel.add(cb);
i1++;
}
}
c.gridx = 0; c.gridy = y;
c.gridwidth = 2;
c.anchor = GridBagConstraints.WEST;
c.insets = getInsets(10, 0, 0, 0);
grid.setConstraints(panel, c);
add(panel);
y++;
}
/** Adds a popup menu.
* @param label the label
* @param items the menu items
* @param defaultItem the menu item initially selected
*/
public void addChoice(final String label, final String[] items, final String defaultItem) {
String label2 = label;
if (label2.indexOf('_')!=-1)
label2 = label2.replace('_', ' ');
final JLabel theLabel = makeLabel(label2);
c.gridx = 0; c.gridy = y;
c.anchor = GridBagConstraints.EAST;
c.gridwidth = 1;
if (choice==null) {
choice = new Vector(4);
c.insets = getInsets(5, 0, 5, 0);
} else
c.insets = getInsets(0, 0, 5, 0);
grid.setConstraints(theLabel, c);
add(theLabel);
// Choice thisChoice = new Choice();
final JComboBox thisChoice = new JComboBox();
thisChoice.addKeyListener(this);
thisChoice.addItemListener(this);
for (int i=0; i<items.length; i++)
thisChoice.addItem(items[i]);
thisChoice.setSelectedItem(defaultItem);
c.gridx = 1; c.gridy = y;
c.anchor = GridBagConstraints.WEST;
grid.setConstraints(thisChoice, c);
add(thisChoice);
choice.addElement(thisChoice);
if (Recorder.record || macro)
saveLabel(thisChoice, label);
y++;
}
/** Adds a message consisting of one or more lines of text. */
public void addMessage(final String text) {
if (text.indexOf('\n')>=0)
// theLabel = new MultiLineLabel(text);
theLabel = new JLabel(text);
else
theLabel = new JLabel(text);
//theLabel.addKeyListener(this);
c.gridx = 0; c.gridy = y;
c.gridwidth = 2;
c.anchor = GridBagConstraints.WEST;
c.insets = getInsets(text.equals("")?0:10, 20, 0, 0);
grid.setConstraints(theLabel, c);
add(theLabel);
y++;
}
/** Adds one or two (side by side) text areas.
* @param text1 initial contents of the first text area
* @param text2 initial contents of the second text area or null
* @param rows the number of rows
* @param rows the number of columns
*/
public void addTextAreas(final String text1, final String text2, final int rows, final int columns) {
if (textArea1!=null) return;
final JPanel panel = new JPanel();
textArea1 = new JTextArea(text1,rows,columns);
if (IJ.isLinux()) textArea1.setBackground(Color.white);
// textArea1.addTextListener(this);
panel.add(textArea1);
if (text2!=null) {
textArea2 = new JTextArea(text2,rows,columns);
if (IJ.isLinux()) textArea2.setBackground(Color.white);
panel.add(textArea2);
}
c.gridx = 0; c.gridy = y;
c.gridwidth = 2;
c.anchor = GridBagConstraints.WEST;
c.insets = new Insets(15, 20, 0, 0);
grid.setConstraints(panel, c);
add(panel);
y++;
}
public void addSlider(final String label, final double minValue, final double maxValue, final double defaultValue) {
int columns = 4;
final int digits = 0;
String label2 = label;
if (label2.indexOf('_')!=-1)
label2 = label2.replace('_', ' ');
final JLabel theLabel = makeLabel(label2);
c.gridx = 0; c.gridy = y;
c.anchor = GridBagConstraints.EAST;
c.gridwidth = 1;
c.insets = new Insets(0, 0, 3, 0);
grid.setConstraints(theLabel, c);
add(theLabel);
if (slider==null) {
slider = new Vector(5);
sliderIndexes = new int[MAX_SLIDERS];
}
final JScrollBar s = new JScrollBar(JScrollBar.HORIZONTAL, (int)defaultValue, 1, (int)minValue, (int)maxValue+1);
slider.addElement(s);
s.addAdjustmentListener(this);
s.setUnitIncrement(1);
if (numberField==null) {
numberField = new Vector(5);
defaultValues = new Vector(5);
defaultText = new Vector(5);
}
if (IJ.isWindows()) columns -= 2;
if (columns<1) columns = 1;
final JTextField tf = new JTextField(IJ.d2s(defaultValue, digits), columns);
if (IJ.isLinux()) tf.setBackground(Color.white);
tf.addActionListener(this);
// tf.addTextListener(this);
tf.addFocusListener(this);
tf.addKeyListener(this);
numberField.addElement(tf);
sliderIndexes[slider.size()-1] = numberField.size()-1;
defaultValues.addElement(new Double(defaultValue));
defaultText.addElement(tf.getText());
tf.setEditable(true);
//if (firstNumericField && firstSlider) tf.selectAll();
firstSlider = false;
final JPanel panel = new JPanel();
final GridBagLayout pgrid = new GridBagLayout();
final GridBagConstraints pc = new GridBagConstraints();
panel.setLayout(pgrid);
// label
//pc.insets = new Insets(5, 0, 0, 0);
//pc.gridx = 0; pc.gridy = 0;
//pc.gridwidth = 1;
//pc.anchor = GridBagConstraints.EAST;
//pgrid.setConstraints(theLabel, pc);
//panel.add(theLabel);
// slider
pc.gridx = 0; pc.gridy = 0;
pc.gridwidth = 1;
pc.ipadx = 75;
pc.anchor = GridBagConstraints.WEST;
pgrid.setConstraints(s, pc);
panel.add(s);
pc.ipadx = 0; // reset
// text field
pc.gridx = 1;
pc.insets = new Insets(5, 5, 0, 0);
pc.anchor = GridBagConstraints.EAST;
pgrid.setConstraints(tf, pc);
panel.add(tf);
grid.setConstraints(panel, c);
c.gridx = 1; c.gridy = y;
c.gridwidth = 1;
c.anchor = GridBagConstraints.WEST;
c.insets = new Insets(0, 0, 0, 0);
grid.setConstraints(panel, c);
add(panel);
y++;
if (Recorder.record || macro)
saveLabel(tf, label);
}
/** Adds a Panel to the dialog. */
public void addPanel(final JPanel panel) {
addPanel(panel , GridBagConstraints.WEST, new Insets(5, 0, 0, 0));
}
/** Adds a Panel to the dialog with custom contraint and insets. The
defaults are GridBagConstraints.WEST (left justified) and
"new Insets(5, 0, 0, 0)" (5 pixels of padding at the top). */
public void addPanel(final JPanel panel, final int contraints, final Insets insets) {
c.gridx = 0; c.gridy = y;
c.gridwidth = 2;
c.anchor = contraints;
c.insets = insets;
grid.setConstraints(panel, c);
add(panel);
y++;
}
/** Set the insets (margins), in pixels, that will be
used for the next component added to the dialog.
<pre>
Default insets:
addMessage: 0,20,0 (empty string) or 10,20,0
addCheckbox: 15,20,0 (first checkbox) or 0,20,0
addCheckboxGroup: 10,0,0
addNumericField: 5,0,3 (first field) or 0,0,3
addStringField: 5,0,5 (first field) or 0,0,5
addChoice: 5,0,5 (first field) or 0,0,5
</pre>
*/
public void setInsets(final int top, final int left, final int bottom) {
topInset = top;
leftInset = left;
bottomInset = bottom;
customInsets = true;
}
/** Sets a replacement label for the "OK" button. */
public void setOKLabel(final String label) {
okLabel = label;
}
/** Make this a "Yes No Cancel" dialog. */
public void enableYesNoCancel() {
yesNoCancel = true;
}
/** No not display "Cancel" button. */
public void hideCancelJButton() {
hideCancelButton = true;
}
Insets getInsets(final int top, final int left, final int bottom, final int right) {
if (customInsets) {
customInsets = false;
return new Insets(topInset, leftInset, bottomInset, 0);
} else
return new Insets(top, left, bottom, right);
}
/** Add an Object implementing the DialogListener interface. This object will
* be notified by its dialogItemChanged method of input to the dialog. The first
* DialogListener will be also called after the user has typed 'OK' or if the
* dialog has been invoked by a macro; it should read all input fields of the
* dialog.
* For other listeners, the OK button will not cause a call to dialogItemChanged;
* the CANCEL button will never cause such a call.
* @param dl the Object that wants to listen.
*/
public void addDialogListener(final DialogListener dl) {
if (dialogListeners == null)
dialogListeners = new Vector();
dialogListeners.addElement(dl);
if (IJ.debugMode) IJ.log("GenericDialog: Listener added: "+dl);
}
/** Returns true if the user clicked on "Cancel". */
public boolean wasCanceled() {
if (wasCanceled)
Macro.abort();
return wasCanceled;
}
/** Returns true if the user has clicked on "OK" or a macro is running. */
public boolean wasOKed() {
return wasOKed || macro;
}
/** Returns the contents of the next numeric field. */
public double getNextNumber() {
if (numberField==null)
return -1.0;
final JTextField tf = (JTextField)numberField.elementAt(nfIndex);
String theText = tf.getText();
String label=null;
if (macro) {
label = (String)labels.get((Object)tf);
theText = Macro.getValue(macroOptions, label, theText);
//IJ.write("getNextNumber: "+label+" "+theText);
}
final String originalText = (String)defaultText.elementAt(nfIndex);
final double defaultValue = ((Double)(defaultValues.elementAt(nfIndex))).doubleValue();
double value;
if (theText.equals(originalText))
value = defaultValue;
else {
final Double d = getValue(theText);
if (d!=null)
value = d.doubleValue();
else {
invalidNumber = true;
errorMessage = "\""+theText+"\" is an invalid number";
value = 0.0;
if (macro) {
IJ.error("Macro Error", "Numeric value expected in run() function\n \n"
+" Dialog: \""+getTitle()+"\"\n"
+" Label: \""+label+"\"\n"
+" Value: \""+theText+"\"");
}
}
}
if (recorderOn)
recordOption(tf, trim(theText));
nfIndex++;
return value;
}
private String trim(String value) {
if (value.endsWith(".0"))
value = value.substring(0, value.length()-2);
if (value.endsWith(".00"))
value = value.substring(0, value.length()-3);
return value;
}
private void recordOption(final JComponent component, String value) {
final String label = (String)labels.get((Object)component);
if (value.equals("")) value = "[]";
Recorder.recordOption(label, value);
}
private void recordCheckboxOption(final JCheckBox cb) {
final String label = (String)labels.get((Object)cb);
if (label!=null) {
if (cb.isSelected()) // checked
Recorder.recordOption(label);
else if (Recorder.getCommandOptions()==null)
Recorder.recordOption(" ");
}
}
protected Double getValue(final String text) {
Double d;
try {d = new Double(text);}
catch (final NumberFormatException e){
d = null;
}
return d;
}
/** Returns true if one or more of the numeric fields contained an
invalid number. Must be called after one or more calls to getNextNumber(). */
public boolean invalidNumber() {
final boolean wasInvalid = invalidNumber;
invalidNumber = false;
return wasInvalid;
}
/** Returns an error message if getNextNumber was unable to convert a
string into a number, otherwise, returns null. */
public String getErrorMessage() {
return errorMessage;
}
/** Returns the contents of the next text field. */
public String getNextString() {
String theText;
if (stringField==null)
return "";
final JTextField tf = (JTextField)(stringField.elementAt(sfIndex));
theText = tf.getText();
if (macro) {
final String label = (String)labels.get((Object)tf);
theText = Macro.getValue(macroOptions, label, theText);
//IJ.write("getNextString: "+label+" "+theText);
}
if (recorderOn)
recordOption(tf, theText);
sfIndex++;
return theText;
}
/** Returns the state of the next checkbox. */
public boolean getNextBoolean() {
if (checkbox==null)
return false;
final JCheckBox cb = (JCheckBox)(checkbox.elementAt(cbIndex));
if (recorderOn)
recordCheckboxOption(cb);
boolean state = cb.isSelected();
if (macro) {
final String label = (String)labels.get((Object)cb);
final String key = Macro.trimKey(label);
state = isMatch(macroOptions, key+" ");
}
cbIndex++;
return state;
}
// Returns true if s2 is in s1 and not in a bracketed literal (e.g., "[literal]")
boolean isMatch(final String s1, String s2) {
if (s1.startsWith(s2))
return true;
s2 = " " + s2;
final int len1 = s1.length();
final int len2 = s2.length();
boolean match, inLiteral=false;
char c;
for (int i=0; i<len1-len2+1; i++) {
c = s1.charAt(i);
if (inLiteral && c==']')
inLiteral = false;
else if (c=='[')
inLiteral = true;
if (c!=s2.charAt(0) || inLiteral || (i>1&&s1.charAt(i-1)=='='))
continue;
match = true;
for (int j=0; j<len2; j++) {
if (s2.charAt(j)!=s1.charAt(i+j))
{match=false; break;}
}
if (match) return true;
}
return false;
}
/** Returns the selected item in the next popup menu. */
public String getNextChoice() {
if (choice==null)
return "";
final JComboBox thisChoice = (JComboBox)(choice.elementAt(choiceIndex));
String item = thisChoice.getSelectedItem().toString();
if (macro) {
final String label = (String)labels.get((Object)thisChoice);
item = Macro.getValue(macroOptions, label, item);
//IJ.write("getNextChoice: "+label+" "+item);
}
if (recorderOn)
recordOption(thisChoice, item);
choiceIndex++;
return item;
}
/** Returns the index of the selected item in the next popup menu. */
public int getNextChoiceIndex() {
if (choice==null)
return -1;
final JComboBox thisChoice = (JComboBox)(choice.elementAt(choiceIndex));
int index = thisChoice.getSelectedIndex();
if (macro) {
final String label = (String)labels.get((Object)thisChoice);
final String oldItem = thisChoice.getSelectedItem().toString();
final int oldIndex = thisChoice.getSelectedIndex();
final String item = Macro.getValue(macroOptions, label, oldItem);
thisChoice.setSelectedItem(item);
index = thisChoice.getSelectedIndex();
if (index==oldIndex && !item.equals(oldItem))
IJ.error(getTitle(), "\""+item+"\" is not a valid choice for \""+label+"\"");
}
if (recorderOn)
recordOption(thisChoice, thisChoice.getSelectedItem().toString());
choiceIndex++;
return index;
}
/** Returns the contents of the next textarea. */
public String getNextText() {
String text;
if (textAreaIndex==0 && textArea1!=null) {
//textArea1.selectAll();
text = textArea1.getText();
textAreaIndex++;
if (macro)
text = Macro.getValue(macroOptions, "text1", text);
if (recorderOn) {
String text2 = text;
final String cmd = Recorder.getCommand();
if (cmd!=null && cmd.equals("Convolve...")) {
text2 = text.replaceAll("\n","\\\\n");
if (!text.endsWith("\n")) text2 = text2 + "\\n";
} else
text2 = text.replace('\n',' ');
Recorder.recordOption("text1", text2);
}
} else if (textAreaIndex==1 && textArea2!=null) {
textArea2.selectAll();
text = textArea2.getText();
textAreaIndex++;
if (macro)
text = Macro.getValue(macroOptions, "text2", text);
if (recorderOn)
Recorder.recordOption("text2", text.replace('\n',' '));
} else
text = null;
return text;
}
/** Displays this dialog box. */
public void showDialog() {
if (macro) {
dispose();
recorderOn = Recorder.record && Recorder.recordInMacros;
} else {
// if (pfr!=null) // prepare preview (not in macro mode): tell the PlugInFilterRunner to listen
// pfr.setDialog(this);
//if (stringField!=null&&numberField==null) {
// TextField tf = (TextField)(stringField.elementAt(0));
// tf.selectAll();
//}
final JPanel buttons = new JPanel();
buttons.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 0));
cancel = new JButton("Cancel");
cancel.addActionListener(this);
cancel.addKeyListener(this);
if (yesNoCancel) {
okLabel = " Yes ";
no = new JButton(" No ");
no.addActionListener(this);
no.addKeyListener(this);
}
okay = new JButton(okLabel);
okay.addActionListener(this);
okay.addKeyListener(this);
if (IJ.isMacintosh()) {
if (yesNoCancel) buttons.add(no);
if (! hideCancelButton)
buttons.add(cancel);
buttons.add(okay);
} else {
buttons.add(okay);
if (yesNoCancel) buttons.add(no);;
if (! hideCancelButton)
buttons.add(cancel);
}
c.gridx = 0; c.gridy = y;
c.anchor = GridBagConstraints.EAST;
c.gridwidth = 2;
c.insets = new Insets(15, 0, 0, 0);
grid.setConstraints(buttons, c);
add(buttons);
if (IJ.isMacintosh())
setResizable(false);
pack();
setup();
if (centerDialog) GUI.center(this);
setVisible(true);
recorderOn = Recorder.record;
IJ.wait(50); // work around for Sun/WinNT bug
}
/* For plugins that read their input only via dialogItemChanged, call it at least once */
if (!wasCanceled && dialogListeners!=null && dialogListeners.size()>0) {
resetCounters();
// ((DialogListener)dialogListeners.elementAt(0)).dialogItemChanged(this, null);
recorderOn = false;
}
resetCounters();
}
/** Reset the counters before reading the dialog parameters */
private void resetCounters() {
nfIndex = 0; // prepare for readout
sfIndex = 0;
cbIndex = 0;
choiceIndex = 0;
textAreaIndex = 0;
invalidNumber = false;
}
/** Returns the Vector containing the numeric TextFields. */
public Vector getNumericFields() {
return numberField;
}
/** Returns the Vector containing the string TextFields. */
public Vector getStringFields() {
return stringField;
}
/** Returns the Vector containing the Checkboxes. */
public Vector getCheckboxes() {
return checkbox;
}
/** Returns the Vector containing the Choices. */
public Vector getChoices() {
return choice;
}
/** Returns the sliders (Scrollbars). */
public Vector getSliders() {
return slider;
}
/** Returns a reference to textArea1. */
public JTextArea getTextArea1() {
return textArea1;
}
/** Returns a reference to textArea2. */
public JTextArea getTextArea2() {
return textArea2;
}
/** Returns a reference to the Label or MultiLineLabel created by the
last addMessage() call, or null if addMessage() was not called. */
public JComponent getMessage() {
return theLabel;
}
/** Returns a reference to the Preview Checkbox. */
public JCheckBox getPreviewCheckbox() {
return previewCheckbox;
}
/** Used by PlugInFilterRunner to provide visable feedback whether preview
is running or not by switching from "Preview" to "wait..."
*/
public void previewRunning(final boolean isRunning) {
if (previewCheckbox!=null) {
previewCheckbox.setLabel(isRunning ? previewRunning : previewLabel);
if (IJ.isMacOSX()) repaint(); //workaround OSX 10.4 refresh bug
}
}
/** Display dialog centered on the primary screen? */
public void centerDialog(final boolean b) {
centerDialog = b;
}
protected void setup() {
}
public void actionPerformed(final ActionEvent e) {
final Object source = e.getSource();
if (source==okay || source==cancel | source==no) {
wasCanceled = source==cancel;
wasOKed = source==okay;
dispose();
} else
notifyListeners(e);
}
public void textValueChanged(final TextEvent e) {
notifyListeners(e);
if (slider==null) return;
final Object source = e.getSource();
for (int i=0; i<slider.size(); i++) {
final int index = sliderIndexes[i];
if (source==numberField.elementAt(index)) {
final JTextField tf = (JTextField)numberField.elementAt(index);
final double value = Tools.parseDouble(tf.getText());
if (!Double.isNaN(value)) {
final JScrollBar sb = (JScrollBar)slider.elementAt(i);
sb.setValue((int)value);
}
//IJ.log(i+" "+tf.getText());
}
}
}
public void itemStateChanged(final ItemEvent e) {
notifyListeners(e);
}
public void focusGained(final FocusEvent e) {
final JComponent c = (JComponent) e.getComponent();
if (c instanceof JTextField)
((JTextField)c).selectAll();
}
public void focusLost(final FocusEvent e) {
final JComponent c = (JComponent) e.getComponent();
if (c instanceof JTextField)
((JTextField)c).select(0,0);
}
public void keyPressed(final KeyEvent e) {
final int keyCode = e.getKeyCode();
IJ.setKeyDown(keyCode);
if (keyCode==KeyEvent.VK_ENTER && textArea1==null) {
wasOKed = true;
if (IJ.isMacOSX()&&IJ.isJava15())
accessTextFields();
dispose();
} else if (keyCode==KeyEvent.VK_ESCAPE) {
wasCanceled = true;
dispose();
IJ.resetEscape();
}
}
void accessTextFields() {
if (stringField!=null) {
for (int i=0; i<stringField.size(); i++)
((JTextField)(stringField.elementAt(i))).getText();
}
if (numberField!=null) {
for (int i=0; i<numberField.size(); i++)
((JTextField)(numberField.elementAt(i))).getText();
}
}
public void keyReleased(final KeyEvent e) {
final int keyCode = e.getKeyCode();
IJ.setKeyUp(keyCode);
final int flags = e.getModifiers();
final boolean control = (flags & KeyEvent.CTRL_MASK) != 0;
final boolean meta = (flags & KeyEvent.META_MASK) != 0;
final boolean shift = (flags & e.SHIFT_MASK) != 0;
if (keyCode==KeyEvent.VK_G && shift && (control||meta))
new ScreenGrabber().run("");
}
public void keyTyped(final KeyEvent e) {}
public Insets getInsets() {
final Insets i= super.getInsets();
return new Insets(i.top+10, i.left+10, i.bottom+10, i.right+10);
}
public synchronized void adjustmentValueChanged(final AdjustmentEvent e) {
final Object source = e.getSource();
for (int i=0; i<slider.size(); i++) {
if (source==slider.elementAt(i)) {
final JScrollBar sb = (JScrollBar)source;
final JTextField tf = (JTextField)numberField.elementAt(sliderIndexes[i]);
tf.setText(""+sb.getValue());
}
}
}
/** Notify any DialogListeners of changes having occurred
* If a listener returns false, do not call further listeners and disable
* the OK button and preview Checkbox (if it exists).
* For PlugInFilters, this ensures that the PlugInFilterRunner,
* which listens as the last one, is not called if the PlugInFilter has
* detected invalid parameters. Thus, unnecessary calling the run(ip) method
* of the PlugInFilter for preview is avoided in that case.
*/
private void notifyListeners(final AWTEvent e) {
if (dialogListeners == null) return;
final boolean everythingOk = true;
for (int i=0; everythingOk && i<dialogListeners.size(); i++)
try {
resetCounters();
// if (!((DialogListener)dialogListeners.elementAt(i)).dialogItemChanged(this, e))
// everythingOk = false; // disable further listeners if false (invalid parameters) returned
}
catch (final Exception err) { // for exceptions, don't cover the input by a window but
IJ.beep(); // show them at in the "Log"
IJ.log("ERROR: "+err+"\nin DialogListener of "+dialogListeners.elementAt(i)+
"\nat "+(err.getStackTrace()[0])+"\nfrom "+(err.getStackTrace()[1])); //requires Java 1.4
}
final boolean workaroundOSXbug = IJ.isMacOSX() && !okay.isEnabled() && everythingOk;
if (previewCheckbox!=null)
previewCheckbox.setEnabled(everythingOk);
okay.setEnabled(everythingOk);
if (workaroundOSXbug) repaint(); // OSX 10.4 bug delays update of enabled until the next input
}
/*public void paint(Graphics g) {
super.paint(g);
if (firstPaint) {
if (numberField!=null && IJ.isMacOSX()) {
// work around for bug on Intel Macs that caused 1st field to be un-editable
JTextField tf = (JTextField)(numberField.elementAt(0));
tf.setEditable(false);
tf.setEditable(true);
}
firstPaint = false;
}
}*/
public void windowClosing(final WindowEvent e) {
wasCanceled = true;
dispose();
}
public void windowActivated(final WindowEvent e) {}
public void windowOpened(final WindowEvent e) {}
public void windowClosed(final WindowEvent e) {}
public void windowIconified(final WindowEvent e) {}
public void windowDeiconified(final WindowEvent e) {}
public void windowDeactivated(final WindowEvent e) {}
}