package jas.hist; import jas.util.JASIcon; import jas.util.JASTextField; import jas.util.PropertyPage; import jas.util.PropertySite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Frame; import java.awt.GridLayout; import java.awt.TextField; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; import java.util.Enumeration; import java.util.Observable; import java.util.Observer; import javax.swing.BorderFactory; import javax.swing.BoundedRangeModel; import javax.swing.DefaultCellEditor; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JProgressBar; import javax.swing.JScrollPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.UIManager; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.event.TableModelEvent; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; class JASHistPropFunctions extends PropertyPage implements ListSelectionListener, PropertySite { public JASHistPropFunctions() { m_init = false; setLayout(new BorderLayout()); JPanel p1 = new JPanel(new BorderLayout()); m_listModel = new DefaultListModel(); m_list = new JList(m_listModel); m_list.setCellRenderer(DataRenderer.createRenderer()); m_list.addListSelectionListener(this); JScrollPane scroll = new JScrollPane(); scroll.setViewportView(m_list); scroll.setPreferredSize(new Dimension(100,120)); p1.add(scroll,BorderLayout.CENTER); JPanel p2 = new JPanel(); JButton add = new JButton("Add..."); add.addActionListener(new AddButtonListener()); add.setMnemonic('d'); p2.add(add); JButton remove = new JButton("Remove"); remove.addActionListener(new RemoveButtonListener()); remove.setMnemonic('R'); p2.add(remove); p1.add(p2,BorderLayout.SOUTH); add(p1,BorderLayout.WEST); JPanel p = new JPanel(new BorderLayout()); JPanel b = new JPanel(new FlowLayout(FlowLayout.LEFT)); m_propStyle = new JASHistPropFunctionStyle(); m_propStyle.setPropertySite(this); b.add(m_propStyle); m_dataStyle = new JASHistPropDataStyle(); m_dataStyle.setPropertySite(this); b.add(m_dataStyle); advanced = new JButton("Advanced..."); advanced.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { try { FunctionAdvancedOptions fao = (FunctionAdvancedOptions) m_selected.getFunction(); Container w = JASHistPropFunctions.this; while (!(w instanceof Frame)) w = w.getParent(); fao.openAdvancedDialog((Frame) w, m_jHist); // The argument pf simply allows the // dialog box to have access to the // PropFunctions fields } catch (Exception exception) {} // do nothing if an exception is thrown } }); advanced.setMnemonic('v'); b.add(advanced); p.add(b,BorderLayout.NORTH); p.setBorder(BorderFactory.createTitledBorder("Function")); m_paramManager = new ParamManager(); p.add(m_paramManager,BorderLayout.CENTER); m_fitManager = new FitManager(); p.add(m_fitManager,BorderLayout.SOUTH); add(p,BorderLayout.CENTER); updateAdvanced(); } public String getHelpTopic() { return "functionsAndFilters.functions"; } public synchronized void doDataExchange(boolean set,Object bean) { if (!m_init) { JASHist hist = (JASHist) bean; Enumeration e = hist.get1DFunctions(); JASHist1DFunctionData d; while (e.hasMoreElements()) { d = (JASHist1DFunctionData) e.nextElement(); m_listModel.addElement(d); Basic1DFunction f = d.getFunction(); if (f instanceof FunctionAdvancedOptions) { f.addObserver(m_listNameChangeListener); } } if (m_listModel.size()>0) { m_selected = (JASHist1DFunctionData) m_listModel.elementAt(0); m_list.setSelectedValue(m_selected,true); } else m_selected = null; m_jHist = hist; m_fitManager.init(hist); m_init = true; } if (m_selected != null) { m_propStyle.doDataExchange(set,m_selected.getStyle()); m_dataStyle.doDataExchange(set,m_selected); m_paramManager.doDataExchange(set,m_selected.getFunction()); m_fitManager.setFunction(m_selected); } setChanged(false); updateAdvanced(); } private synchronized void addFunction(JASHist1DFunctionData d) { m_selected = d; m_listModel.addElement(d); m_list.setSelectedValue(d,true); doDataExchange(true,m_jHist); } private synchronized void removeFunction() { m_selected.delete(); m_listModel.removeElement(m_selected); doDataExchange(true, m_jHist); } public void valueChanged(ListSelectionEvent evt) { if (!m_init) return; doDataExchange(true,m_jHist); if (m_listModel.getSize() > 0) { m_selected = (JASHist1DFunctionData) m_list.getSelectedValue(); doDataExchange(false,m_jHist); updateAdvanced(); } } private void updateAdvanced() { advanced.setEnabled(m_selected != null && m_selected.getFunction() instanceof FunctionAdvancedOptions); } public void callEnable() { setChanged(true); } protected void deactivate() { m_paramManager.deactivate(); m_fitManager.deactivate(); } private class AddButtonListener implements ActionListener { public void actionPerformed(ActionEvent evt) { FunctionRegistry fr = FunctionRegistry.instance(); Container w = JASHistPropFunctions.this; while (!(w instanceof Frame)) w = w.getParent(); FunctionFactory ff = fr.chooseFunction((Frame) w); if (ff != null) { try { Basic1DFunction f = ff.createFunction(m_jHist); JASHistData d = m_jHist.addData(f); d.show(true); addFunction((JASHist1DFunctionData) d); if (f instanceof FunctionAdvancedOptions) f.addObserver(m_listNameChangeListener); } catch (FunctionFactoryError x) { x.printStackTrace(); // ???? } } } } private class RemoveButtonListener implements ActionListener { public void actionPerformed(ActionEvent evt) { removeFunction(); } } private class ParamTableModel extends AbstractTableModel implements Observer { ParamTableModel() { format.setMaximumFractionDigits(6); } void setFunction(Basic1DFunction f) { // We want to observe the function, so that we can update if the // function changes (e.g. due to a fit), but we need to be careful // to remove the link when we are deactivated. if (f != null) f.deleteObserver(this); this.f = f; f.addObserver(this); fireTableChanged(new TableModelEvent(this, -1)); } void deactivate() { if (f != null) f.deleteObserver(this); } public void update(Observable obs, Object arg) { if (obs == f) { fireTableChanged(new TableModelEvent(this, -1)); } } public int getRowCount() { if (f==null) return 0; String[] names = f.getParameterNames(); return (names == null) ? 0 : names.length; } public int getColumnCount() { return columns.length; } public Object getValueAt(int row, int col) { if (col == 0) return f.getParameterNames()[row]; else if (col == 1) return format.format(f.getParameterValues()[row]); else if (col == 2) { boolean value; if (f instanceof Fittable1DFunction) { value = ((Fittable1DFunction) f).getIncludeParametersInFit()[row]; } else value = false; return new Boolean(value); } else if (col == 3) { if (f instanceof Fittable1DFunction) { Fittable1DFunction func = (Fittable1DFunction) f; Fitter fit = func.getFit(); if (fit != null) { boolean[] inFit = func.getIncludeParametersInFit(); if (! inFit[row]) return "Not in fit"; int i = 0, j = 0; while (i < row) { if (inFit[i]) j++; i++; } return format.format(fit.getParameterSigmas()[j]); } } } return null; } public String getColumnName(int col) { return columns[col]; } public Class getColumnClass(int col) { if (col == 2) return Boolean.class; if (col == 1) return String.class; return super.getColumnClass(col); } public boolean isCellEditable(int row, int col) { return (col == 2 || col == 1); } public void setValueAt(Object value, int row, int col) { if (col == 1) { try { double d = Double.valueOf((String) value).doubleValue(); f.setParameter(row,d); } catch (InvalidFunctionParameter e) {getToolkit().beep();} catch (NumberFormatException e) {getToolkit().beep();} } else if (col == 2) { if (f instanceof Fittable1DFunction) { boolean b = ((Boolean) value).booleanValue(); ((Fittable1DFunction) f).setIncludeParameterInFit(row,b); } else getToolkit().beep(); } } private String[] columns = {"Parameter","Value","Fit","Error"}; private Basic1DFunction f = null; } private class ParamManager extends JPanel { ParamManager() { this.setLayout(new BorderLayout()); m_model = new ParamTableModel(); m_table = new JTable(m_model); m_table.setAutoCreateColumnsFromModel(false); TableColumn col = m_table.getColumn("Value"); col.setCellRenderer(new ParamCellRenderer()); col.setCellEditor(new DefaultCellEditor(new JTextField(""))); //col = m_table.getColumn("Fit"); //col.setHeaderRenderer(new FitHeaderRenderer(m_table, col.getHeaderRenderer())); //col.sizeWidthToFit(); // just wide enough for header cell JScrollPane scrollpane = new JScrollPane(m_table); scrollpane.setPreferredSize(new Dimension(350,100)); this.add("Center",scrollpane); } void doDataExchange(boolean set,Basic1DFunction f) { if (m_f != f) { m_model.setFunction(f); m_f = f; } } void deactivate() { m_model.deactivate(); m_f = null; } private JTable m_table; private Basic1DFunction m_f = null; private ParamTableModel m_model; private class ParamCellRenderer implements TableCellRenderer { public Component getTableCellRendererComponent(JTable table, Object obj, boolean sel, boolean hasFocus, int col, int row) /* * bug: hasFocus not used */ { if (obj != null) text.setText(obj.toString()); if (sel) { text.setBackground(UIManager.getColor("textHighlight")); text.setForeground(UIManager.getColor("textHighlightText")); } else { text.setBackground(b); text.setForeground(f); } text.setScrollOffset(0); return text; } private JASTextField text = new JASTextField(); private Color f = text.getForeground(); private Color b = text.getBackground(); } private class FitHeaderRenderer implements TableCellRenderer { FitHeaderRenderer(JTable table, TableCellRenderer renderer) { m_table = table; m_renderer = renderer; } public Component getTableCellRendererComponent(JTable table, Object obj, boolean sel, boolean hasFocus, int col, int row) { if (table == null) table = m_table; return m_renderer.getTableCellRendererComponent(table, obj, sel, hasFocus, col, row); } private TableCellRenderer m_renderer; private JTable m_table; } } private class FitManager extends JPanel implements Observer,ItemListener { FitManager() { states = new javax.swing.Icon[5]; states[Fitter.FIT] = JASIcon.create(this,"tick.gif"); states[Fitter.FAILED] = JASIcon.create(this,"cross.gif"); states[Fitter.FITTING] = JASIcon.create(this,"running.gif"); states[Fitter.READYTOFIT] = states[Fitter.FIT]; states[Fitter.NOTREADYTOFIT] = states[Fitter.FAILED]; JPanel p4 = new JPanel(); m_state = new JLabel(states[Fitter.FIT]); p4.add(m_state); m_chi2 = new JLabel(" ") { public Dimension getPreferredSize() { Dimension result = super.getPreferredSize(); result.width = 100; // Leave sufficient room return result; } }; m_chi2.setIcon(JASIcon.create(this,"chi2.gif")); p4.add(m_chi2); JPanel p2 = new JPanel(new BorderLayout()); p2.add("North",p4); JProgressBar bar = new JProgressBar(); m_fitWatcher = new FitWatcher(bar.getModel()); p2.add("Center",bar); JPanel p3 = new JPanel(); p3.add(new JLabel("Using")); m_fitterChoice = new JComboBox(); m_fitterChoice.addItemListener(this); p3.add(m_fitterChoice); JPanel p1 = new JPanel(); p1.add(new JLabel("Data")); m_choice = new JComboBox(); p1.add(m_choice); JPanel p5 = new JPanel(new GridLayout(0,1)); p5.add(p3); p5.add(p1); this.setLayout(new BorderLayout()); m_fit = new JCheckBox("Fit"); m_fit.addActionListener(new FitNowHandler()); m_fit.setMnemonic('F'); // It would be nice to make the title contain the // fit checkbox, needs custom swing border //this.setBorder(new CheckBoxBorder(m_fit)); this.setBorder(BorderFactory.createTitledBorder("Fit")); this.add("North",m_fit); this.add("West",p5); this.add("Center",p2); //add("South",new Checkbox("Update fit when data changes")); setEnabled(false); } void init(JASHist hist) { Enumeration e = hist.getDataSources(); if (e.hasMoreElements()) { m_choice.setRenderer(DataRenderer.createRenderer()); m_choice.addItemListener(this); } else m_choice.setEnabled(false); while (e.hasMoreElements()) { JASHist1DHistogramData d = (JASHist1DHistogramData) e.nextElement(); m_choice.addItem(d.getFittableDataSource()); } e = FitterRegistry.instance().elements(); while (e.hasMoreElements()) { FitterFactory ff = (FitterFactory) e.nextElement(); m_fitterChoice.addItem(ff); } m_fitterChoice.setSelectedItem(FitterRegistry.instance().getDefaultFitterFactory()); } void setFunction(JASHist1DFunctionData fd) { if (m_function != null) m_function.deleteObserver(this); m_fitWatcher.clearFit(); if (fd != null && fd.getFunction() instanceof Fittable1DFunction && m_choice.getItemCount() > 0 && m_fitterChoice.getItemCount() > 0) { m_fd = fd; m_function = (Fittable1DFunction) fd.getFunction(); m_function.addObserver(this); Fitter fitter = m_function.getFit(); if (fitter != null) m_fitWatcher.setFit(fitter); update(); setEnabled(true); } else { setEnabled(false); m_function = null; m_fd = null; } } private void update() { Fitter fitter = m_function.getFit(); m_fit.setSelected(fitter != null); if (fitter != null) { m_chi2.setText(format.format(fitter.getChiSquared())); //TO-DO make sure all fitters get data assigned (?). Object data = fitter.getData(); if ( data != null ) m_choice.setSelectedItem(data); } } public void update(Observable obs, Object arg) { //System.out.println("FitManager update"+obs+" "+m_function); if (obs == m_function) update(); } void deactivate() { //System.out.println("Deactivating FitManager"); setFunction(null); } public void setEnabled(boolean state) { m_fit.setEnabled(state); m_choice.setEnabled(state); m_fitterChoice.setEnabled(state); } public void itemStateChanged(ItemEvent e) { // TODO: This causes problems when ComboBox is changed programatically! //if (m_function != null) m_function.clearFit(); //if (m_fit != null && m_fit.isSelected()) m_fit.setSelected(false); } private Fittable1DFunction m_function; private JASHist1DFunctionData m_fd; private JLabel m_state; private JCheckBox m_fit; private JComboBox m_choice; private JComboBox m_fitterChoice; private JLabel m_chi2; private FitWatcher m_fitWatcher; private javax.swing.Icon states[]; private class FitNowHandler implements ActionListener { public void actionPerformed(ActionEvent e) { if (m_fit.isSelected()) { FitterFactory ff = (FitterFactory) m_fitterChoice.getSelectedItem(); Fitter fitter = ff.createFitter(); fitter.setFunction(m_function); XYDataSource data = (XYDataSource) m_choice.getSelectedItem(); fitter.setData(data); m_fitWatcher.setFit(fitter); fitter.start(); } else { m_fitWatcher.clearFit().dispose(); } } } private class FitWatcher implements Observer { FitWatcher(BoundedRangeModel model) { model.setMinimum(0); model.setMaximum(100); this.model = model; clearFit(); } void setFit(Fitter fit) { m_fitter = fit; m_fitter.addObserver(this); m_state.setEnabled(true); m_state.repaint(); // Not needed? Swing 0.6.1 m_chi2.setEnabled(true); m_chi2.repaint(); // Not needed? Swing 0.6.1 } Fitter clearFit() { Fitter fit = m_fitter; if (fit != null) { fit.deleteObserver(this); m_fitter = null; } m_state.setEnabled(false); m_state.repaint(); // Not needed? Swing 0.6.1 m_chi2.setEnabled(false); m_chi2.repaint(); // Not needed? Swing 0.6.1 return fit; } public void update(Observable obs, Object arg) { if (arg instanceof FitUpdate) { FitUpdate fu = (FitUpdate) arg; int state = fu.getState(); if (state == Fitter.OUTAHERE) clearFit(); else { model.setValue(fu.getPercent()); m_state.setIcon(states[state]); if (state == Fitter.FAILED) { Container w = JASHistPropFunctions.this; while (!(w instanceof Frame)) w = w.getParent(); JOptionPane.showMessageDialog(w, fu.getReason(), "Fit error...", JOptionPane.ERROR_MESSAGE); } } } } private BoundedRangeModel model; private Fitter m_fitter; } } public JASHist getHist() { return m_jHist; } private JASHistPropFunctionStyle m_propStyle; private JASHistPropDataStyle m_dataStyle; private ParamManager m_paramManager; private FitManager m_fitManager; private boolean m_init; private JASHist1DFunctionData m_selected; private JList m_list; private DefaultListModel m_listModel; private TextField m_value; private JASHist m_jHist; private java.text.NumberFormat format = java.text.NumberFormat.getInstance(); private JButton advanced; private ListNameChangeListener m_listNameChangeListener = new ListNameChangeListener(); private class ListNameChangeListener implements Observer { public void update(Observable o, Object arg) { m_list.repaint(); } } }