package org.signalml.app.view.common.components.presets; import static org.signalml.app.util.i18n.SvarogI18n._; import java.awt.BorderLayout; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import javax.swing.AbstractAction; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JTextField; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; import javax.swing.event.DocumentEvent; import org.signalml.app.SvarogApplication; import org.signalml.app.config.ApplicationConfiguration; import org.signalml.app.config.preset.Preset; import org.signalml.app.config.preset.PresetComboBoxModel; import org.signalml.app.config.preset.PresetManager; import org.signalml.app.util.IconUtils; import org.signalml.app.view.common.components.AnyChangeDocumentAdapter; import org.signalml.app.view.common.components.panels.AbstractPanel; import org.signalml.app.view.common.dialogs.AbstractDialog; import org.signalml.app.view.common.dialogs.AbstractPresetDialog; import org.signalml.app.view.common.dialogs.errors.Dialogs; import org.signalml.plugin.export.SignalMLException; import org.signalml.util.Util; public class CompactPresetControlsPanel extends AbstractPanel { public static String CHOOSE_PRESET_OPTION = _("<< select to load preset >>"); /** * The dialog/panel which is controlled by this preset control panel * (e.g. the presets selected in this PresetControlsPanel affects * the presetPanel). */ protected PresetableView presetPanel; /** * the {@link PresetManager manager} of {@link Preset presets} */ protected PresetManager presetManager; /** * the model for {@link #presetComboBox} */ private PresetComboBoxModel presetComboBoxModel; /** * the combo box which allows to select the {@link Preset preset} */ private JComboBox presetComboBox; protected JButton savePresetButton; protected JButton removePresetButton; /** * the dialog that allows to select the {@link #getPresetComboBox preset} * and specify the name for it */ private ChoosePresetDialog choosePresetDialog; public CompactPresetControlsPanel(PresetManager presetManager, PresetableView presetPanel) { super(); this.presetManager = presetManager; this.presetPanel = presetPanel; createInterface(); } protected void createInterface() { setBorder(new TitledBorder("Preset")); add(getPresetComboBox()); add(getSavePresetButton()); add(getRemovePresetButton()); } public JComboBox getPresetComboBox() { if (presetComboBox == null) { presetComboBox = new JComboBox(getPresetComboBoxModel()); presetComboBox.setPreferredSize(new Dimension(240, 26)); presetComboBox.addActionListener(new LoadPresetAction()); resetPresetComboBoxSelection(); } return presetComboBox; } public PresetComboBoxModel getPresetComboBoxModel() { if (presetComboBoxModel == null) { presetComboBoxModel = new PresetComboBoxModel(CHOOSE_PRESET_OPTION, presetManager); } return presetComboBoxModel; } public JButton getSavePresetButton() { if (savePresetButton == null) { savePresetButton = new JButton(new SavePresetAction()); } return savePresetButton; } public JButton getRemovePresetButton() { if (removePresetButton == null) removePresetButton = new JButton(new RemovePresetAction()); return removePresetButton; } public ChoosePresetDialog getChoosePresetDialog() { if (choosePresetDialog == null) choosePresetDialog = new ChoosePresetDialog(); return choosePresetDialog; } /** * Returns the {@link ApplicationConfiguration configuration} of Svarog. * @return the configuration of Svarog */ public ApplicationConfiguration getApplicationConfig() { return SvarogApplication.getApplicationConfiguration(); } /** * (Creates and) returns the current {@link Preset preset}. * Must be specified in the implementing class. * @return the current preset * @throws SignalMLException TODO never thrown in implementations (???) */ public Preset getPresetFromMainPanel() throws SignalMLException { return presetPanel.getPreset(); } /** * Sets the given preset as the current {@link Preset preset}. * Fills all necessary fields of the dialog with the data from this preset. * Must be specified in the implementing class. * @param preset the preset to use as the new current preset. * @throws SignalMLException TODO never thrown in implementations */ private void setPresetToMainPanel(Preset preset) throws SignalMLException { if (presetPanel.isPresetCompatible(preset)) presetPanel.setPreset(preset); else resetPresetComboBoxSelection(); } public void resetPresetComboBoxSelection() { getPresetComboBox().setSelectedIndex(0); } /** * Action that saves the {@link AbstractPresetDialog#getPreset() current * preset} to the list of presets. */ protected class SavePresetAction extends AbstractAction { private static final long serialVersionUID = 1L; /** * Constructor. Sets the icon and the tool-tip text for this action. */ public SavePresetAction() { super(); putValue(AbstractAction.SMALL_ICON, IconUtils.loadClassPathIcon("org/signalml/app/icon/preset_save.png")); putValue(AbstractAction.SHORT_DESCRIPTION,_("Save")); } /** * Called when this action is performed: * <ul> * <li>{@link AbstractPresetDialog#getPreset() gets} the current * {@link Preset preset} from the dialog,</li> * <li>{@link ChoosePresetDialog#getName(String, boolean) gets} the * name for this preset,</li> * <li>if the preset of such name exists asks if it should be replaced, * </li> * <li>saves the preset to the {@link PresetManager}.</li> * </ul> */ public void actionPerformed(ActionEvent ev) { Preset preset; try { preset = getPresetFromMainPanel(); } catch (SignalMLException ex) { logger.error("Failed to get preset", ex); Dialogs.showExceptionDialog(CompactPresetControlsPanel.this, ex); return; } if (preset == null) { return; } if (!presetManager.getPresetClass().isAssignableFrom(preset.getClass())) { throw new ClassCastException("Bad preset class"); } String newName = getChoosePresetDialog().getName(preset.getName(), true); if (newName == null) { return; } Preset existingPreset = presetManager.getPresetByName(newName); if (existingPreset != null) { final String msg = _("Preset already exists, do you really want to overwrite this preset?"); if (Dialogs.showWarningYesNoDialog(msg) == Dialogs.DIALOG_OPTIONS.NO) { return; } } preset.setName(newName); presetManager.setPreset(preset); presetComboBoxModel.setSelectedItem(preset); if (getApplicationConfig().isSaveConfigOnEveryChange()) { try { presetManager.writeToPersistence(null); } catch (IOException ex) { logger.error("Failed to save preset configuration", ex); } } } } /** * Action that {@link AbstractPresetDialog#setPreset(Preset) sets} * the selected {@link Preset preset} as active. */ protected class LoadPresetAction extends AbstractAction { private static final long serialVersionUID = 1L; /** * Constructor. Sets the icon and the tool-tip text for this action. */ public LoadPresetAction() { super(); putValue(AbstractAction.SMALL_ICON, IconUtils.loadClassPathIcon("org/signalml/app/icon/preset_load.png")); putValue(AbstractAction.SHORT_DESCRIPTION,_("Load")); } /** * Called when the action is performed: * <ul> * <li>gets the selected {@link Preset preset} or if there is no this * functions ends, * </li> * <li>if there were changes in the dialog or dialog doesn't know if * there were, shows the warning to the user that the current preset * will be overridden and allows him to cancel the operation,</li> * <li>{@link AbstractPresetDialog#setPreset(Preset) sets} this preset * as the active one (fills the dialog with the data from it).</li> * </ul> */ public void actionPerformed(ActionEvent ev) { int index = getPresetComboBox().getSelectedIndex(); if (index <= 0) { return; } Preset preset = presetManager.getPresetAt(index-1); getPresetComboBox().repaint(); if (preset == null) { return; } try { setPresetToMainPanel(preset); } catch (SignalMLException ex) { logger.error("Failed to set preset", ex); Dialogs.showExceptionDialog(CompactPresetControlsPanel.this, ex); return; } } } /** * Action that removes the selected preset. * If there is no selected preset does nothing. * If there is warns the user before the removal. */ protected class RemovePresetAction extends AbstractAction { private static final long serialVersionUID = 1L; /** * Constructor. Sets the icon and the tool-tip text for this action. */ public RemovePresetAction() { super(); putValue(AbstractAction.SMALL_ICON, IconUtils.loadClassPathIcon("org/signalml/app/icon/preset_remove.png")); putValue(AbstractAction.SHORT_DESCRIPTION,_("Remove")); } /** * Called when the action is performed: * <ul> * <li>gets the selected {@link Preset preset} or if there is no this * functions ends, * </li> * <li>warns the user and if the user cancels operation this function * ends,</li> * <li>removes the selected preset.</li> * </ul> */ public void actionPerformed(ActionEvent ev) { String name = getChoosePresetDialog().getName(null, false); if (name == null) { return; } Preset preset = presetManager.getPresetByName(name); if (preset == null) { return; } presetManager.removePresetByName(name); if (getApplicationConfig().isSaveConfigOnEveryChange()) { try { presetManager.writeToPersistence(null); } catch (IOException ex) { logger.error("Failed to save preset configuration", ex); } } } } /** * Dialog that allows to select the {@link #getPresetComboBox preset} * and specify the name for it. */ protected class ChoosePresetDialog extends AbstractDialog { private static final long serialVersionUID = 1L; /** * the model for a combo box that allows to select the preset */ private PresetComboBoxModel presetComboBoxModel; /** * the combo box that allows to select the preset */ protected JComboBox presetComboBox; /** * the text field to specify the name for the preset */ private JTextField nameTextField; protected boolean editable = true; /** * Constructor. Sets the message source from enclosing class and * uses the enclosing class as the parent window to this dialog. */ protected ChoosePresetDialog() { super(CompactPresetControlsPanel.this.getParentWindow(), true); } /** * Returns if the text field with the name of the preset should be * editable. * @return {@code true} if the text field with the name of the * preset should be editable, {@code false} otherwise */ public boolean isEditable() { return editable; } /** * Sets if the text field with the name of the preset should be * editable. * @param editable {@code true} if the text field with the name of the * preset should be editable, {@code false} otherwise */ public void setEditable(boolean editable) { if (this.editable != editable) { this.editable = editable; getNameTextField().setEditable(editable); } } @Override public void fillDialogFromModel(Object model) { // do nothing } @Override public void fillModelFromDialog(Object model) { // do nothing } @Override protected void initialize() { setTitle(_("Select preset")); super.initialize(); } /** * Adds the panel with 3 elements: * <ul> * <li>icon with a question mark,</li> * <li>{@link #getPresetComboBox() combo box} to select the preset,</li> * <li>{@link #getNameTextField() text field} with the name of the * preset (may be editable or not).</li> * </ul> */ @Override public JComponent createInterface() { JPanel interfacePanel = new JPanel(new BorderLayout()); interfacePanel.setBorder(new CompoundBorder( new TitledBorder(_("Select preset name")), new EmptyBorder(3,3,3,3) )); JPanel inputPanel = new JPanel(); inputPanel.setBorder(new EmptyBorder(0,8,0,0)); inputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.Y_AXIS)); inputPanel.add(getPresetComboBox()); inputPanel.add(Box.createVerticalStrut(10)); inputPanel.add(getNameTextField()); JLabel iconLabel = new JLabel(IconUtils.getQuestionIcon()); iconLabel.setVerticalAlignment(JLabel.TOP); interfacePanel.add(iconLabel, BorderLayout.WEST); interfacePanel.add(inputPanel, BorderLayout.CENTER); return interfacePanel; } /** * Returns the {@link PresetComboBoxModel model} for a combo box * to select presets. * If the model doesn't exist it is created. * @return the model */ public PresetComboBoxModel getPresetComboBoxModel() { if (presetComboBoxModel == null) { presetComboBoxModel = new PresetComboBoxModel(_("<< select to choose preset >>"), presetManager); } return presetComboBoxModel; } /** * If the preset combo box already exists it is simply returned. * If it doesn't, it is created. * Created combo box contains the listener, which sets the * {@link #getNameTextField() name field} depending on the selected * preset. * @return the preset combo box */ public JComboBox getPresetComboBox() { if (presetComboBox == null) { ; presetComboBox = new JComboBox(getPresetComboBoxModel()); presetComboBox.setSelectedIndex(0); presetComboBox.setPreferredSize(new Dimension(200,25)); presetComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { int index = presetComboBox.getSelectedIndex(); if (index <= 0) { return; } Preset p = presetManager.getPresetAt(index-1); if (p != null) { JTextField nameTextField = getNameTextField(); nameTextField.setText(p.getName()); if (editable) { nameTextField.selectAll(); nameTextField.requestFocusInWindow(); } } presetComboBox.setSelectedIndex(0); presetComboBox.repaint(); } }); } return presetComboBox; } /** * Returns the text field with the name of the preset. * If the field doesn't exist it is created. * If this name contains at least one character the OK button is * activated. * @return the text field with the name of the preset. */ public JTextField getNameTextField() { if (nameTextField == null) { nameTextField = new JTextField(); nameTextField.setPreferredSize(new Dimension(200,25)); nameTextField.getDocument().addDocumentListener(new AnyChangeDocumentAdapter() { @Override public void anyUpdate(DocumentEvent e) { getOkAction().setEnabled(e.getDocument().getLength() > 0); } }); } return nameTextField; } /** * Shows this dialog and returns the entered name. * @param initialName the name that (if it is not empty) will be * set as the value of {@link #getNameTextField() name text field} * @param editable {@code true} if the name text field should be * editable, {@code false} otherwise * @return the specified name or null if there is no name (or the name * is empty) */ public String getName(String initialName, boolean editable) { initializeNow(); setEditable(editable); JTextField nameTextField = getNameTextField(); if (initialName != null && !initialName.isEmpty()) { nameTextField.setText(initialName); getOkAction().setEnabled(true); } else { nameTextField.setText(""); getOkAction().setEnabled(false); } if (editable) { nameTextField.selectAll(); nameTextField.requestFocusInWindow(); } boolean ok = showDialog(null, true); if (!ok) { return null; } String name = nameTextField.getText(); name = Util.trimString(name); if (name == null || name.isEmpty()) { return null; } return name; } @Override public boolean supportsModelClass(Class<?> clazz) { return (clazz == null); } } }