edu.cmu.tetradapp.app.NumberFormatAction Maven / Gradle / Ivy
///////////////////////////////////////////////////////////////////////////////
// For information as to what this class does, see the Javadoc, below. //
// Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, //
// 2007, 2008, 2009, 2010, 2014, 2015, 2022 by Peter Spirtes, Richard //
// Scheines, Joseph Ramsey, and Clark Glymour. //
// //
// This program is free software; you can redistribute it and/or modify //
// it under the terms of the GNU General Public License as published by //
// the Free Software Foundation; either version 2 of the License, or //
// (at your option) any later version. //
// //
// This program 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 General Public License for more details. //
// //
// You should have received a copy of the GNU General Public License //
// along with this program; if not, write to the Free Software //
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA //
///////////////////////////////////////////////////////////////////////////////
package edu.cmu.tetradapp.app;
import edu.cmu.tetrad.util.JOptionUtils;
import edu.cmu.tetrad.util.NumberFormatUtil;
import javax.swing.*;
import javax.swing.border.TitledBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.Transferable;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.prefs.Preferences;
/**
* Presents a dialog allowing the user to change the number format used to render real numbers throughout Tetrad.
*
* @author josephramsey
*/
final class NumberFormatAction extends AbstractAction
implements ClipboardOwner {
/**
* This is a class field because it's accessed from multiple places.
*/
private final JTextField formatField = new JTextField(
Preferences.userRoot().get("numberFormat", "0.0000"));
/**
* Constructs a new number format action.
*/
public NumberFormatAction() {
super("Number Format");
}
/**
* Pops up a dialog that lets the user decide how to render real numbers. A basic and an advanced version are
* available.
*/
public void actionPerformed(ActionEvent e) {
// Set up basic tab.
final double sample = 23.5;
JTextField renderFieldBasic = new JTextField(
new DecimalFormat(constructSimpleFormatString()).format(sample));
renderFieldBasic.setMaximumSize(new Dimension(150, 50));
renderFieldBasic.setEditable(false);
renderFieldBasic.setBackground(Color.WHITE);
JCheckBox scientific = new JCheckBox(
"Use scientific notation",
Preferences.userRoot().getBoolean("scientificNotation", false));
scientific.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
JCheckBox checkBox = (JCheckBox) e.getSource();
Preferences.userRoot().putBoolean("scientificNotation",
checkBox.isSelected());
renderFieldBasic.setText(new DecimalFormat(constructSimpleFormatString()).format(sample));
NumberFormatAction.this.formatField.setText(constructSimpleFormatString());
}
});
SpinnerModel model = new SpinnerNumberModel(
Preferences.userRoot().getInt("numDecimals", 4), 0, 300, 1);
JSpinner numDecimals = new JSpinner(model);
numDecimals.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JSpinner spinner = (JSpinner) e.getSource();
SpinnerNumberModel model = (SpinnerNumberModel) spinner.getModel();
int value = (Integer) model.getValue();
Preferences.userRoot().putInt("numDecimals", value);
renderFieldBasic.setText(new DecimalFormat(constructSimpleFormatString()).format(sample));
NumberFormatAction.this.formatField.setText(constructSimpleFormatString());
}
});
numDecimals.setMaximumSize(numDecimals.getPreferredSize());
boolean decimalsOptional = Preferences.userRoot().getBoolean("decimalsOptimal", false);
JRadioButton optional = new JRadioButton("Optional", decimalsOptional);
JRadioButton fixed = new JRadioButton("Fixed", !decimalsOptional);
ButtonGroup group = new ButtonGroup();
group.add(optional);
group.add(fixed);
optional.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Preferences.userRoot().putBoolean("decimalsOptional", true);
renderFieldBasic.setText(new DecimalFormat(constructSimpleFormatString()).format(sample));
NumberFormatAction.this.formatField.setText(constructSimpleFormatString());
}
});
fixed.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Preferences.userRoot().putBoolean("decimalsOptional", false);
renderFieldBasic.setText(new DecimalFormat(constructSimpleFormatString()).format(sample));
NumberFormatAction.this.formatField.setText(constructSimpleFormatString());
}
});
if (!Preferences.userRoot().getBoolean("numFormatAdvanced", false)) {
this.formatField.setText(constructSimpleFormatString());
}
// Set up basic panel.
Box a = Box.createVerticalBox();
a.setBorder(new TitledBorder("Simple Formats"));
Box a2 = Box.createHorizontalBox();
a2.add(scientific);
a2.add(Box.createHorizontalGlue());
a.add(a2);
Box a3 = Box.createHorizontalBox();
a3.add(new JLabel("Number of decimal places = "));
a3.add(numDecimals);
a3.add(Box.createHorizontalGlue());
a.add(a3);
Box a4 = Box.createHorizontalBox();
a4.add(Box.createRigidArea(new Dimension(20, 0)));
a4.add(optional);
a4.add(Box.createHorizontalGlue());
a.add(a4);
Box a5 = Box.createHorizontalBox();
a5.add(Box.createRigidArea(new Dimension(20, 0)));
a5.add(fixed);
a5.add(Box.createHorizontalGlue());
a.add(a5);
a.add(Box.createVerticalStrut(20));
Box a6 = Box.createHorizontalBox();
a6.add(new JLabel("Renders as: "));
a6.add(renderFieldBasic);
a.add(a6);
a.add(Box.createVerticalGlue());
Box basic = Box.createVerticalBox();
basic.add(a);
JPanel basicPanel = new JPanel();
basicPanel.setLayout(new BorderLayout());
basicPanel.add(basic, BorderLayout.CENTER);
// Set up advanced panel.
JTextField sampleFieldAdvanced = new JTextField("" + sample);
JTextField renderFieldAdvanced = new JTextField(getNumberFormat().format(sample));
renderFieldAdvanced.setEditable(false);
renderFieldAdvanced.setBackground(Color.WHITE);
this.formatField.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
updateAdvancedFields(sampleFieldAdvanced, renderFieldAdvanced);
}
});
sampleFieldAdvanced.addKeyListener(new KeyAdapter() {
public void keyReleased(KeyEvent e) {
updateAdvancedFields(sampleFieldAdvanced, renderFieldAdvanced);
}
});
Box z = Box.createVerticalBox();
Box z1 = Box.createHorizontalBox();
z1.add(new JLabel("Refer to DecimalFormat in the "));
z1.add(Box.createHorizontalGlue());
z.add(z1);
Box z2 = Box.createHorizontalBox();
z2.add(new JLabel("documentation for Java."));
z2.add(Box.createHorizontalGlue());
z.add(z2);
z.add(Box.createVerticalStrut(10));
Box advanced = Box.createVerticalBox();
advanced.add(z);
Box f = Box.createVerticalBox();
f.setBorder(new TitledBorder("Format String"));
Box f1 = Box.createHorizontalBox();
f1.add(this.formatField);
f.add(f1);
advanced.add(f);
Box c = Box.createVerticalBox();
c.setBorder(new TitledBorder("Example"));
Box c1 = Box.createHorizontalBox();
c1.add(sampleFieldAdvanced);
c.add(c1);
Box c2 = Box.createHorizontalBox();
c2.add(new JLabel("Renders as: "));
c2.add(Box.createHorizontalGlue());
c.add(c2);
Box c3 = Box.createHorizontalBox();
c3.add(renderFieldAdvanced);
c.add(c3);
advanced.add(c);
JPanel advancedPanel = new JPanel();
advancedPanel.setLayout(new BorderLayout());
advancedPanel.add(advanced, BorderLayout.CENTER);
JTabbedPane tabbedPane = new JTabbedPane();
tabbedPane.addTab("Basic", basicPanel);
tabbedPane.addTab("Advanced", advancedPanel);
if (Preferences.userRoot().getBoolean("numFormatAdvanced", false)) {
tabbedPane.setSelectedComponent(advancedPanel);
}
tabbedPane.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent e) {
JTabbedPane tabbedPane = (JTabbedPane) e.getSource();
JPanel panel = (JPanel) tabbedPane.getSelectedComponent();
Preferences.userRoot().putBoolean("numFormatAdvanced", panel == advancedPanel);
if (panel == basicPanel) {
String format = constructSimpleFormatString();
NumberFormatAction.this.formatField.setText(format);
renderFieldBasic.setText(new DecimalFormat(format).format(sample));
}
}
});
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add(tabbedPane, BorderLayout.CENTER);
JOptionPane.showMessageDialog(JOptionUtils.centeringComp(),
panel, "Formatting for All Real Numbers", JOptionPane.INFORMATION_MESSAGE);
NumberFormatUtil.getInstance().setNumberFormat(getNumberFormat());
}
/**
* @return the most recent number format, whether from the basic or from the advanced tab.
* @throws RuntimeException if the getModel format string cannot be accepted by DecimalFormat.
*/
private NumberFormat getNumberFormat() throws IllegalStateException {
return new DecimalFormat(getFormatString());
}
/**
* @return the most recent format string, whether from the basic or from the advanced tab.
* @throws RuntimeException if the stored format string cannot be accepted by DecimalFormat.
*/
private String getFormatString() throws RuntimeException {
String format = this.formatField.getText();
try {
new DecimalFormat(format);
} catch (Exception e) {
throw new RuntimeException("Illegal format string: " + format);
}
return format;
}
/**
* Required by the AbstractAction interface; does nothing.
*/
public void lostOwnership(Clipboard clipboard, Transferable contents) {
}
/**
* This sets up the behavior in the advanced tab where when you change the format or the example numnber, it updates
* the rendering. It also sets the color of the format or example to red when it's ill-formatted.
*
* @param sampleFieldAdvanced The example field.
* @param renderFieldAdvanced The render field.
*/
private void updateAdvancedFields(JTextField sampleFieldAdvanced, JTextField renderFieldAdvanced) {
try {
Double.parseDouble(sampleFieldAdvanced.getText());
} catch (Exception e1) {
sampleFieldAdvanced.setForeground(Color.RED);
return;
}
String format = this.formatField.getText();
try {
new DecimalFormat(format);
} catch (Exception e2) {
this.formatField.setForeground(Color.RED);
return;
}
double sample = Double.parseDouble(sampleFieldAdvanced.getText());
NumberFormat nf = new DecimalFormat(format);
renderFieldAdvanced.setText(nf.format(sample));
sampleFieldAdvanced.setForeground(Color.BLACK);
this.formatField.setForeground(Color.BLACK);
Preferences.userRoot().put("numberFormat", format);
}
private String constructSimpleFormatString() {
boolean scientificNotation = Preferences.userRoot()
.getBoolean("scientificNotation", false);
int numDecimals = Preferences.userRoot()
.getInt("numDecimals", 4);
boolean optional = Preferences.userRoot()
.getBoolean("decimalsOptional", false);
StringBuilder buf = new StringBuilder();
buf.append("0.");
if (optional) {
for (int i = 0; i < numDecimals; i++) {
buf.append("#");
}
} else {
for (int i = 0; i < numDecimals; i++) {
buf.append("0");
}
}
if (scientificNotation) {
buf.append("E0");
}
String formatString = buf.toString();
// formatField.setText(formatString);
Preferences.userRoot().put("numberFormat", formatString);
return formatString;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy