/* BookZoomSettingsPanel.java created 2007-12-17 * */ package org.signalml.app.view.book; import static org.signalml.app.util.i18n.SvarogI18n._; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Font; import java.text.ParseException; import javax.swing.Box; import javax.swing.GroupLayout; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; import javax.swing.SpinnerNumberModel; import javax.swing.GroupLayout.Alignment; import javax.swing.JSpinner.DefaultEditor; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.log4j.Logger; import org.signalml.app.view.book.tools.ZoomBookTool; import org.signalml.app.view.common.components.TitledCrossBorder; import org.signalml.domain.book.StandardBookSegment; import org.springframework.validation.Errors; /** * Panel to set how the {@link BookView} should be zoomed. * Contains two sub-panels: * <ul> * <li>the panel with ranges of position and frequency that should be * displayed,</li> * <li>the panel with check-box if the zoom ratio should be preserved * on drag.</li> * </ul> * * @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o. */ public class BookZoomSettingsPanel extends JPanel { private static final long serialVersionUID = 1L; protected static final Logger logger = Logger.getLogger(BookZoomSettingsPanel.class); /** * the model for {@link #minPositionSpinner} */ private SpinnerNumberModel minPositionModel; /** * the model for {@link #maxPositionSpinner} */ private SpinnerNumberModel maxPositionModel; /** * the model for {@link #minFrequencySpinner} */ private SpinnerNumberModel minFrequencyModel; /** * the model for {@link #maxFrequencySpinner} */ private SpinnerNumberModel maxFrequencyModel; /** * the spinner for the minimal displayed position (in points) */ private JSpinner minPositionSpinner; /** * the spinner for the maximal displayed position (in points) */ private JSpinner maxPositionSpinner; /** * the spinner for the minimal displayed frequency TODO jednostka */ private JSpinner minFrequencySpinner; /** * the spinner for the maximal displayed frequency TODO jednostka */ private JSpinner maxFrequencySpinner; /** * the check-box if the zoom ratio should be preserved * TODO probably not used */ private JCheckBox preserveRatioCheckBox; /** * the maximal value of the position that can be set in the spinners */ private double positionLimit; /** * the maximal value of the frequency that can be set in the spinners */ private double frequencyLimit; /** * {@code true} if this panel should contain a cross which closes this * panel, {@code false} otherwise */ private boolean hasCloseCross; /** * Constructor. Sets the source of messages, if this panel should contain * the closing cross and initializes this panel * @param hasCloseCross @code true} if this panel should contain a cross * which closes this panel, {@code false} otherwise */ public BookZoomSettingsPanel(boolean hasCloseCross) { super(); this.hasCloseCross = hasCloseCross; initialize(); } /** * Initializes this panel with two sub-panels: * <ul> * <li>the panel with ranges of position and frequency that should be * displayed,</li> * <li>the panel with check-box if the zoom ratio should be preserved * on drag.</li> * </ul> */ private void initialize() { setLayout(new BorderLayout()); JPanel rangePanel = new JPanel(); rangePanel.setBorder(new CompoundBorder( new TitledCrossBorder(_("Range"), hasCloseCross), new EmptyBorder(3,3,3,3) )); GroupLayout layout = new GroupLayout(rangePanel); rangePanel.setLayout(layout); layout.setAutoCreateContainerGaps(false); layout.setAutoCreateGaps(true); JLabel positionLabel = new JLabel(_("Position")); JLabel frequencyLabel = new JLabel(_("Frequency")); JLabel minPositionLabel = new JLabel(_("min")); JLabel minFrequencyLabel = new JLabel(_("min")); JLabel maxPositionLabel = new JLabel(_("max")); JLabel maxFrequencyLabel = new JLabel(_("max")); Component positionGlue = Box.createHorizontalGlue(); Component frequencyGlue = Box.createHorizontalGlue(); GroupLayout.SequentialGroup hGroup = layout.createSequentialGroup(); hGroup.addGroup( layout.createParallelGroup() .addComponent(positionLabel) .addComponent(frequencyLabel) ); hGroup.addGroup( layout.createParallelGroup() .addComponent(minPositionLabel) .addComponent(minFrequencyLabel) ); hGroup.addGroup( layout.createParallelGroup() .addComponent(getMinPositionSpinner()) .addComponent(getMinFrequencySpinner()) ); hGroup.addGroup( layout.createParallelGroup() .addComponent(positionGlue) .addComponent(frequencyGlue) ); hGroup.addGroup( layout.createParallelGroup() .addComponent(maxPositionLabel) .addComponent(maxFrequencyLabel) ); hGroup.addGroup( layout.createParallelGroup() .addComponent(getMaxPositionSpinner()) .addComponent(getMaxFrequencySpinner()) ); layout.setHorizontalGroup(hGroup); GroupLayout.SequentialGroup vGroup = layout.createSequentialGroup(); vGroup.addGroup( layout.createParallelGroup(Alignment.BASELINE) .addComponent(positionLabel) .addComponent(minPositionLabel) .addComponent(getMinPositionSpinner()) .addComponent(positionGlue) .addComponent(maxPositionLabel) .addComponent(getMaxPositionSpinner()) ); vGroup.addGroup( layout.createParallelGroup(Alignment.BASELINE) .addComponent(frequencyLabel) .addComponent(minFrequencyLabel) .addComponent(getMinFrequencySpinner()) .addComponent(frequencyGlue) .addComponent(maxFrequencyLabel) .addComponent(getMaxFrequencySpinner()) ); layout.setVerticalGroup(vGroup); JPanel preservationPanel = new JPanel(new FlowLayout()); preservationPanel.setBorder(new CompoundBorder( new TitledBorder(_("Ratio preservation")), new EmptyBorder(3,3,3,3) )); preservationPanel.add(getPreserveRatioCheckBox()); add(rangePanel, BorderLayout.CENTER); add(preservationPanel, BorderLayout.SOUTH); Dimension size = getPreferredSize(); if (size.width < 150) { size.width = 150; } setPreferredSize(size); } /** * Returns the model for the {@link #getMinPositionSpinner() minimum * position spinner}. * If the model doesn't exist it is created with parameters: * <ul> * <li>minimum {@code = 0},</li> * <li>maximum {@code = 19.99},</li> * <li>step {@code = 0.01},</li> * <li>value {@code = 0}</li></ul> * @return the model for the minimum position spinner */ public SpinnerNumberModel getMinPositionModel() { if (minPositionModel == null) { minPositionModel = new SpinnerNumberModel(0.0, 0.0, 20.0-0.01, 0.01); } return minPositionModel; } /** * Returns the minimum position spinner. * If the spinner doesn't exist it is created: * <ul> * <li>with the specified {@link #getMinPositionModel() model},</li> * <li>to have specified size (80x25 pixel),</li> * <li>with the listener which updates the value of the {@link * #getMaxPositionSpinner() maximum position spinner} to be at least * {@code 0.01} larger then the value of this spinner.</li></ul> * @return the minimum position spinner */ public JSpinner getMinPositionSpinner() { if (minPositionSpinner == null) { minPositionSpinner = new JSpinner(getMinPositionModel()); Dimension spinnerSize = new Dimension(80,25); minPositionSpinner.setPreferredSize(spinnerSize); minPositionSpinner.setMinimumSize(spinnerSize); minPositionSpinner.setMaximumSize(spinnerSize); minPositionSpinner.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { double value = ((Number) minPositionSpinner.getValue()).doubleValue(); double otherValue = ((Number) getMaxPositionSpinner().getValue()).doubleValue(); if ((value+0.01) > otherValue) { getMaxPositionSpinner().setValue(value + 0.01); } } }); minPositionSpinner.setEditor(new JSpinner.NumberEditor(minPositionSpinner, "0.00")); minPositionSpinner.setFont(minPositionSpinner.getFont().deriveFont(Font.PLAIN)); } return minPositionSpinner; } /** * Returns the model for the {@link #getMaxPositionSpinner() maximum * position spinner}. * If the model doesn't exist it is created with parameters: * <ul> * <li>minimum {@code = 0.01},</li> * <li>maximum {@code = 20},</li> * <li>step {@code = 0.01},</li> * <li>value {@code = 20}</li></ul> * @return the model for the minimum position spinner */ public SpinnerNumberModel getMaxPositionModel() { if (maxPositionModel == null) { maxPositionModel = new SpinnerNumberModel(20.0, 0.01, 20.0, 0.01); } return maxPositionModel; } /** * Returns the maximum position spinner. * If the spinner doesn't exist it is created: * <ul> * <li>with the specified {@link #getMaxPositionModel() model},</li> * <li>to have specified size (80x25 pixel),</li> * <li>with the listener which updates the value of the {@link * #getMinPositionSpinner() maximum position spinner} to be at least * {@code 0.01} smaller then the value of this spinner.</li></ul> * @return the maximum position spinner */ public JSpinner getMaxPositionSpinner() { if (maxPositionSpinner == null) { maxPositionSpinner = new JSpinner(getMaxPositionModel()); Dimension spinnerSize = new Dimension(80,25); maxPositionSpinner.setPreferredSize(spinnerSize); maxPositionSpinner.setMinimumSize(spinnerSize); maxPositionSpinner.setMaximumSize(spinnerSize); maxPositionSpinner.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { double value = ((Number) maxPositionSpinner.getValue()).doubleValue(); double otherValue = ((Number) getMinPositionSpinner().getValue()).doubleValue(); if ((value-0.01) < otherValue) { getMinPositionSpinner().setValue(value - 0.01); } } }); maxPositionSpinner.setEditor(new JSpinner.NumberEditor(maxPositionSpinner, "0.00")); maxPositionSpinner.setFont(maxPositionSpinner.getFont().deriveFont(Font.PLAIN)); } return maxPositionSpinner; } /** * Returns the model for the {@link #getMinFrequencySpinner() minimum * frequency spinner}. * If the model doesn't exist it is created with parameters: * <ul> * <li>minimum {@code = 0},</li> * <li>maximum {@code 63.99},</li> * <li>step {@code = 0.01},</li> * <li>value {@code = 0}</li></ul> * @return the model for the minimum position spinner */ public SpinnerNumberModel getMinFrequencyModel() { if (minFrequencyModel == null) { minFrequencyModel = new SpinnerNumberModel(0.0, 0.0, 64.0-0.01, 0.01); } return minFrequencyModel; } /** * Returns the minimum frequency spinner. * If the spinner doesn't exist it is created: * <ul> * <li>with the specified {@link #getMinFrequencyModel() model},</li> * <li>to have specified size (80x25 pixel),</li> * <li>with the listener which updates the value of the {@link * #getMaxFrequencySpinner() maximum frequency spinner} to be at least * {@code 0.01} larger then the value of this spinner.</li></ul> * @return the minimum frequency spinner */ public JSpinner getMinFrequencySpinner() { if (minFrequencySpinner == null) { minFrequencySpinner = new JSpinner(getMinFrequencyModel()); Dimension spinnerSize = new Dimension(80,25); minFrequencySpinner.setPreferredSize(spinnerSize); minFrequencySpinner.setMinimumSize(spinnerSize); minFrequencySpinner.setMaximumSize(spinnerSize); minFrequencySpinner.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { double value = ((Number) minFrequencySpinner.getValue()).doubleValue(); double otherValue = ((Number) getMaxFrequencySpinner().getValue()).doubleValue(); if ((value+0.01) > otherValue) { getMaxFrequencySpinner().setValue(value + 0.01); } } }); minFrequencySpinner.setEditor(new JSpinner.NumberEditor(minFrequencySpinner, "0.00")); minFrequencySpinner.setFont(minFrequencySpinner.getFont().deriveFont(Font.PLAIN)); } return minFrequencySpinner; } /** * Returns the model for the {@link #getMaxPositionSpinner() maximum * position spinner}. * If the model doesn't exist it is created with parameters: * <ul> * <li>minimum {@code = 0.01},</li> * <li>maximum {@code = 64},</li> * <li>step {@code = 0.01},</li> * <li>value {@code = 64}</li></ul> * @return the model for the minimum position spinner */ public SpinnerNumberModel getMaxFrequencyModel() { if (maxFrequencyModel == null) { maxFrequencyModel = new SpinnerNumberModel(64.0, 0.01, 64.0, 0.01); } return maxFrequencyModel; } /** * Returns the maximum frequency spinner. * If the spinner doesn't exist it is created: * <ul> * <li>with the specified {@link #getMaxFrequencyModel() model},</li> * <li>to have specified size (80x25 pixel),</li> * <li>with the listener which updates the value of the {@link * #getMinFrequencySpinner() minimum frequency spinner} to be at least * {@code 0.01} smaller then the value of this spinner.</li></ul> * @return the maximum frequency spinner */ public JSpinner getMaxFrequencySpinner() { if (maxFrequencySpinner == null) { maxFrequencySpinner = new JSpinner(getMaxFrequencyModel()); Dimension spinnerSize = new Dimension(80,25); maxFrequencySpinner.setPreferredSize(spinnerSize); maxFrequencySpinner.setMinimumSize(spinnerSize); maxFrequencySpinner.setMaximumSize(spinnerSize); maxFrequencySpinner.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { double value = ((Number) maxFrequencySpinner.getValue()).doubleValue(); double otherValue = ((Number) getMinFrequencySpinner().getValue()).doubleValue(); if ((value-0.01) < otherValue) { getMinFrequencySpinner().setValue(value - 0.01); } } }); maxFrequencySpinner.setEditor(new JSpinner.NumberEditor(maxFrequencySpinner, "0.00")); maxFrequencySpinner.setFont(maxFrequencySpinner.getFont().deriveFont(Font.PLAIN)); } return maxFrequencySpinner; } /** * Returns the check-box if the zoom ratio should be preserved. * If the button doesn't exist it is created. * @return the check-box if the zoom ratio should be preserved */ public JCheckBox getPreserveRatioCheckBox() { if (preserveRatioCheckBox == null) { preserveRatioCheckBox = new JCheckBox(_("Preserve scale ratio on drag")); } return preserveRatioCheckBox; } /** * Using the given {@link BookView model} fills the fields of this panel: * <ul> * <li>the maximum of {@link #getMinPositionModel() minimum} and {@link * #getMaxPositionModel() maximum} position model,</li> * <li>the maximum of {@link #getMinFrequencyModel() minimum} and {@link * #getMaxFrequencyModel() maximum} frequency model,</li> * <li>the state of {@link #getPreserveRatioCheckBox() preserve zoom ratio * check-box}.</li> * </ul> * @param view the model */ public void fillPanelFromModel(BookView view) { BookPlot plot = view.getPlot(); StandardBookSegment segment = plot.getSegment(); if (segment == null) { return; } positionLimit = segment.getSegmentLength(); frequencyLimit = view.getDocument().getBook().getSamplingFrequency()/2; getMinPositionModel().setMaximum(positionLimit - 0.01); getMaxPositionModel().setMaximum(positionLimit); getMinFrequencyModel().setMaximum(frequencyLimit - 0.01); getMaxFrequencyModel().setMaximum(frequencyLimit); getMinPositionSpinner().setValue(plot.getMinPosition()); getMaxPositionSpinner().setValue(plot.getMaxPosition()); getMinFrequencySpinner().setValue(plot.getMinFrequency()); getMaxFrequencySpinner().setValue(plot.getMaxFrequency()); getPreserveRatioCheckBox().setSelected(view.getZoomBookTool().isPreserveRatio()); } /** * Stores the values of the spinners in the {@link BookView model}. * In order to do it * <ul> * <li>commits the edited values to all spinners,</li> * <li>sets the {@link BookPlot#setZoom(double, double, double, double) * zoom} in the {@link BookPlot book plot},</li> * <li>sets the {@link ZoomBookTool#setPreserveRatio(boolean) preserve * ratio} in the {@link ZoomBookTool}.</li></ul> * @param view the model */ public void fillModelFromPanel(BookView view) { BookPlot plot = view.getPlot(); StandardBookSegment segment = plot.getSegment(); if (segment == null) { return; } // XXX update broken spinners... try { getMinPositionSpinner().commitEdit(); } catch (ParseException ex) { JComponent editor = getMinPositionSpinner().getEditor(); if (editor instanceof DefaultEditor) { ((DefaultEditor) editor).getTextField().setValue(getMinPositionSpinner().getValue()); } } try { getMaxPositionSpinner().commitEdit(); } catch (ParseException ex) { JComponent editor = getMaxPositionSpinner().getEditor(); if (editor instanceof DefaultEditor) { ((DefaultEditor) editor).getTextField().setValue(getMaxPositionSpinner().getValue()); } } try { getMinFrequencySpinner().commitEdit(); } catch (ParseException ex) { JComponent editor = getMinFrequencySpinner().getEditor(); if (editor instanceof DefaultEditor) { ((DefaultEditor) editor).getTextField().setValue(getMinFrequencySpinner().getValue()); } } try { getMaxFrequencySpinner().commitEdit(); } catch (ParseException ex) { JComponent editor = getMaxFrequencySpinner().getEditor(); if (editor instanceof DefaultEditor) { ((DefaultEditor) editor).getTextField().setValue(getMaxFrequencySpinner().getValue()); } } double minPosition = ((Number) getMinPositionSpinner().getValue()).doubleValue(); double maxPosition = ((Number) getMaxPositionSpinner().getValue()).doubleValue(); double minFrequency = ((Number) getMinFrequencySpinner().getValue()).doubleValue(); double maxFrequency = ((Number) getMaxFrequencySpinner().getValue()).doubleValue(); plot.setZoom(minPosition, maxPosition, minFrequency, maxFrequency); view.getZoomBookTool().setPreserveRatio(getPreserveRatioCheckBox().isSelected()); } /** * Validates this panel. This panel is always valid. * @param errors the variable in which errors are stored */ public void validate(Errors errors) { // do nothing } }