package org.reprap.gui;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.GridLayout;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.JScrollPane;
import javax.swing.JComboBox;
import javax.swing.Box;
import java.awt.Button;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import org.reprap.utilities.Debug;
/**
* This reads in the preferences file and constructs a set of menus from it to allow entries
* to be edited.
*
* Preference keys either start with the string "Extruder" followed by a number
* and an underscore (that is, they look like "Extruder3_temp(C)") in which case they
* are assumed to be a characteristic of the extruder with that number; or they don't,
* in which case they are assumed to be global characteristics of the entire machine.
*
* The keys should end with their dimensions: "Extruder3_temp(C)", "Axis2Scale(steps/mm)", but
* regrettably can't contain un-escaped space characters (see java.util.Properties).
*
* Some weak type checking is done to prevent obvious crassness being put in the edit
* boxes. This is done at save time and prevents the junk being written, but doesn't give
* a chance to correct it.
*
* Extensively adapted from Simon's old version by Adrian to construct itself from
* the preferences file.
*
*/
//Boxes must contain one of three types:
enum Category
{
number, string, bool;
}
public class Preferences extends JFrame {
private static final long serialVersionUID = 1L;
// Load of arrays for all the stuff...
private int extruderCount;
private JLabel[] globals; // Array of JLabels for the general key names
private PreferencesValue[] globalValues; // Array of JTextFields for the general variables
private Category[] globalCats; // What are they?
private JLabel[][] extruders; // Array of Arrays of JLabels for the extruders' key names
private PreferencesValue[][] extruderValues; // Array of Arrays of JTextFields for the extruders' variables
private Category[][] extruderCats; // What are they?
// Get the show on the road...
public static void main(String[] args)
{
new Preferences();
}
/**
* Get the value corresponding to name from the preferences file
* @param name
* @return String
*/
private String loadString(String name) throws IOException {
return org.reprap.Preferences.loadGlobalString(name);
}
/**
* Save the value corresponding to name to the preferences file
* @param name
* @param value
*/
private void saveString(String name, String value) throws IOException {
org.reprap.Preferences.setGlobalString(name, value);
}
public void updatePreferencesValues() {
try {
for(int i = 0; i < globals.length; i++)
{
globalValues[i].setText(loadString(globals[i].getText()));
}
for(int j = 0; j < extruderCount; j++)
{
JLabel[] enames = extruders[j];
for(int i = 0; i < enames.length; i++)
extruderValues[j][i].setText(loadString(enames[i].getText()));
}
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, "Updating preferences: " + ex);
ex.printStackTrace();
}
}
/**
* Save the lot to the preferences file
*
*/
public void savePreferences() {
try {
for(int i = 0; i < globals.length; i++)
{
String s = globalValues[i].getText();
if(category(s) != globalCats[i])
Debug.e("Preferences window: Dud format for " + globals[i].getText() + ": " + s);
else
saveString(globals[i].getText(), s);
}
for(int j = 0; j < extruderCount; j++)
{
JLabel[] enames = extruders[j];
PreferencesValue[] evals = extruderValues[j];
Category[] cats = extruderCats[j];
for(int i = 0; i < enames.length; i++)
{
String s = evals[i].getText();
if(category(s) != cats[i])
Debug.e("Preferences window: Dud format for " + enames[i].getText() + ": " + s);
else
saveString(enames[i].getText(), s);
}
}
org.reprap.Preferences.saveGlobal();
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, "Saving preferences: " + ex);
ex.printStackTrace();
}
}
/**
* Constructor loads all the information from the preferences file,
* converts it into arrays of JPanels and JTextFields, then builds the
* menus from them.
*
* @param frame
*/
public Preferences()
{
// Start with everything that isn't an extruder value.
try {
String[] g = org.reprap.Preferences.notStartsWith("Extruder");
Arrays.sort(g);
globals = makeLabels(g);
globalValues = makeValues(globals);
globalCats = categorise(globalValues);
}catch (Exception ex)
{
Debug.e("Preferences window: Can't load the globals!");
ex.printStackTrace();
}
// Next we need to know how many extruders we've got.
try{
extruderCount = Integer.parseInt(loadString("NumberOfExtruders"));
} catch (Exception ex)
{
Debug.e("Preferences window: Can't load the extruder count!");
ex.printStackTrace();
}
// Now build a set of arrays for each extruder in turn.
extruders= new JLabel[extruderCount][];
extruderValues= new PreferencesValue[extruderCount][];
extruderCats = new Category[extruderCount][];
try {
for(int i = 0; i < extruderCount; i++)
{
String[] a = org.reprap.Preferences.startsWith("Extruder" + i);
Arrays.sort(a);
extruders[i] = makeLabels(a);
extruderValues[i]= makeValues(extruders[i]);
extruderCats[i] = categorise(extruderValues[i]);
}
}catch (Exception ex)
{
Debug.e("Preferences window: Can't load extruder(s)!");
ex.printStackTrace();
}
// Paint the lot on the screen...
initGUI();
//Utility.centerWindowOnParent(this, frame);
}
private JButton OKButton()
{
JButton jButtonOK = new JButton();
jButtonOK.setText("OK");
jButtonOK.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent evt)
{
jButtonOKMouseClicked(evt);
}
});
return jButtonOK;
}
private JButton CancelButton()
{
JButton jButtonCancel = new JButton();
jButtonCancel.setText("Cancel");
jButtonCancel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent evt)
{
jButtonCancelMouseClicked(evt);
}
});
return jButtonCancel;
}
private void addValueToPanel(PreferencesValue value, JPanel panel)
{
if(isBoolean(value.getText()))
{
value.makeBoolean();
panel.add(value.getObject());
}
else
panel.add(value.getObject());
}
/**
* Set up the panels with all the right boxes in
*
*/
private void initGUI()
{
setSize(400, 500);
//Dimension box = new Dimension(30, 10);
// Put it all together
try {
// combobox with buttons for selecting config files
JPanel panel = new JPanel();
String[] configfiles = { "reprap.properties" };
File dir = new File( org.reprap.Preferences.getPropsFolderPath());
if (dir.list() != null)
{
configfiles = dir.list();
for (int i=0; i<configfiles.length; i++)
{
if(configfiles[i].indexOf(".properties") != -1)
configfiles[i] = configfiles[i].substring(0, configfiles[i].indexOf(".properties"));
}
}
final JComboBox configfileList = new JComboBox(configfiles);
configfileList.setEditable(true);
String configName = org.reprap.Preferences.getDefaultPropsFile();
configName = configName.substring(0, configName.indexOf(".properties"));
configfileList.setSelectedItem(configName);
configfileList.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if ("comboBoxChanged".equals(e.getActionCommand()))
{
String configName = (String)configfileList.getSelectedItem() + ".properties";
String configPath = org.reprap.Preferences.getPropsFolderPath() + configName;
if((new File(configPath)).exists())
{
Debug.d("loading config " + configName);
org.reprap.Preferences.loadConfig(configName);
updatePreferencesValues();
}
}
}
});
panel.add(new JLabel("preferences file:"));
Button prefCreateButton = new Button("create");
prefCreateButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String configName = (String)configfileList.getSelectedItem() + ".properties";
String configPath = org.reprap.Preferences.getPropsFolderPath() + configName;
File configFileObj = new File(configPath);
if(!configFileObj.exists())
{
configfileList.addItem(configfileList.getSelectedItem());
Debug.a("loading config " + configName);
org.reprap.Preferences.loadConfig(configName);
updatePreferencesValues();
}
}
});
Button prefDeleteButton = new Button("delete");
prefDeleteButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
String configName = (String)configfileList.getSelectedItem() + ".properties";
if(!configName.equals("reprap.properties"))
{
String configPath = org.reprap.Preferences.getPropsFolderPath() + configName;
File configFileObj = new File(configPath);
if(configFileObj.exists())
{
configFileObj.delete();
configfileList.removeItem(configfileList.getSelectedItem());
updatePreferencesValues();
}
else
{
configName = org.reprap.Preferences.getDefaultPropsFile();
configName = configName.substring(0, configName.indexOf(".properties"));
configfileList.setSelectedItem(configName);
}
}
}
});
panel.add(configfileList);
panel.add(prefCreateButton);
panel.add(prefDeleteButton);
// We'll have a tab for the globals, then one
// for each extruder
Box prefDiffBox = new Box(1);
JTabbedPane jTabbedPane1 = new JTabbedPane();
prefDiffBox.add(panel);
prefDiffBox.add(jTabbedPane1);
add(prefDiffBox);
// Do the global panel
JPanel jPanelGeneral = new JPanel();
JScrollPane jScrollPaneGeneral = new JScrollPane(jPanelGeneral);
boolean odd = globals.length%2 != 0;
int rows;
if(odd)
rows = globals.length/2 + 2;
else
rows = globals.length/2 + 1;
jPanelGeneral.setLayout(new GridLayout(rows, 4, 5, 5));
jTabbedPane1.addTab("Globals", null, jScrollPaneGeneral, null);
// Do it in two chunks, so they're vertically ordered, not horizontally
int half = globals.length/2;
int next;
int i;
for(i = 0; i < half; i++)
{
jPanelGeneral.add(globals[i]);
addValueToPanel(globalValues[i], jPanelGeneral);
next = i + half;
if(next < globals.length)
{
jPanelGeneral.add(globals[next]);
addValueToPanel(globalValues[next], jPanelGeneral);
}
}
if(odd)
{
jPanelGeneral.add(globals[globals.length - 1]);
jPanelGeneral.add(globalValues[globals.length - 1].getObject());
jPanelGeneral.add(new JLabel());
jPanelGeneral.add(new JLabel());
}
jPanelGeneral.add(OKButton());
jPanelGeneral.add(new JLabel());
jPanelGeneral.add(new JLabel());
jPanelGeneral.add(CancelButton());
jPanelGeneral.setSize(600, 700);
// Do all the extruder panels
for(int j = 0; j < extruderCount; j++)
{
JLabel[] keys = extruders[j];
PreferencesValue[] values = extruderValues[j];
JPanel jPanelExtruder = new JPanel();
JScrollPane jScrollPaneExtruder = new JScrollPane(jPanelExtruder);
odd = keys.length%2 != 0;
if(odd)
rows = keys.length/2 + 2;
else
rows = keys.length/2 + 1;
jPanelExtruder.setLayout(new GridLayout(rows, 4, 5, 5));
//jTabbedPane1.addTab("Extruder " + j, null, jScrollPaneExtruder, null);
jTabbedPane1.addTab(loadString("Extruder" + j +"_MaterialType(name)"), null, jScrollPaneExtruder, null);
// Do it in two chunks, so they're vertically ordered, not horizontally
half = keys.length/2;
for(i = 0; i < keys.length/2; i++)
{
jPanelExtruder.add(keys[i]);
addValueToPanel(values[i], jPanelExtruder);
next = i + half;
if(next < keys.length)
{
jPanelExtruder.add(keys[next]);
addValueToPanel(values[next], jPanelExtruder);
}
}
if(odd)
{
jPanelExtruder.add(keys[keys.length - 1]);
jPanelExtruder.add(values[keys.length - 1].getObject());
jPanelExtruder.add(new JLabel());
jPanelExtruder.add(new JLabel());
}
jPanelExtruder.add(OKButton());
jPanelExtruder.add(new JLabel());
jPanelExtruder.add(new JLabel());
jPanelExtruder.add(CancelButton());
jPanelExtruder.setSize(600, 700);
}
} catch (Exception e) {
e.printStackTrace();
}
// Wrap it all up
//getContentPane().setLayout(null);
setTitle("RepRap Preferences");
// setSize(xall, yall);
pack();
}
/**
* What to do when OK is clicked
* @param evt
*/
private void jButtonOKMouseClicked(MouseEvent evt) {
// Update all preferences
savePreferences();
dispose();
}
/**
* What to do when Cancel is clicked
* @param evt
*/
private void jButtonCancelMouseClicked(MouseEvent evt) {
// Close without saving
dispose();
}
/**
* Take an array of strings and turn them into labels (right justified).
* @param a
* @return
*/
private JLabel[] makeLabels(String[] a)
{
JLabel[] result = new JLabel[a.length];
for(int i = 0; i < a.length; i++)
{
result[i] = new JLabel();
result[i].setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
result[i].setText(a[i]);
}
return result;
}
/**
* Take an array of labels and use their string values as keys to look up
* the corresponding values. Make those into an array of editable boxes.
* @param a
* @return
*/
private PreferencesValue[] makeValues(JLabel[] a)
{
PreferencesValue[] result = new PreferencesValue[a.length];
String value;
for(int i = 0; i < a.length; i++)
{
try{
value = loadString(a[i].getText());
result[i] = new PreferencesValue(new JTextField());
result[i].setText(value);
} catch (Exception ex)
{
ex.printStackTrace();
}
}
return result;
}
/**
* Is a string saying a boolean?
* @param s
* @return
*/
private boolean isBoolean(String s)
{
if(s.equalsIgnoreCase("true"))
return true;
if(s.equalsIgnoreCase("false"))
return true;
return false;
}
/**
* Is a string a number (int or double)?
*
* There must be a better way to do this; also this doesn't allow
* for 1.3e-5...
*
* @param s
* @return
*/
private boolean isNumber(String s)
{
// Bulletproofing.
if ((s==null)||(s.length()==0)) return false;
int start = 0;
while(Character.isSpaceChar(s.charAt(start)))
start++;
if(s.charAt(start) == '-' || s.charAt(start) == '+')
start++;
// Last we checked, only one decimal point allowed per number.
int dotCount = 0;
for(int i = start; i < s.length(); i++)
{
char c = s.charAt(i);
if(!Character.isDigit(c))
{
if(c != '.')
return false;
else
{
dotCount++;
if(dotCount > 1)
return false;
}
}
}
return true;
}
/**
* Find if a string is a boolean, a number, or a string
* @param s
* @return
*/
private Category category(String s)
{
if(isBoolean(s))
return Category.bool;
if(isNumber(s))
return Category.number;
return Category.string;
}
/**
* Generate an array of categories corresponsing to the text in
* an array of edit boxes so they can be checked later.
* @param a
* @return
*/
private Category[] categorise(PreferencesValue[] a)
{
Category[] result = new Category[a.length];
for(int i = 0; i < a.length; i++)
result[i] = category(a[i].getText());
return result;
}
}