/* EditMontageReferencePanel.java created 2007-10-24 * */ package org.signalml.app.view.montage; 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.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.AbstractAction; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; import org.apache.log4j.Logger; import org.signalml.app.model.components.validation.ValidationErrors; import org.signalml.app.model.montage.MontageGeneratorListModel; import org.signalml.app.util.IconUtils; import org.signalml.app.view.common.components.CompactButton; import org.signalml.app.view.common.components.ResolvableComboBox; import org.signalml.app.view.common.dialogs.errors.Dialogs; import org.signalml.app.view.common.dialogs.errors.ValidationErrorsDialog; import org.signalml.domain.montage.Montage; import org.signalml.domain.montage.MontageException; import org.signalml.domain.montage.SourceChannel; import org.signalml.domain.montage.SourceMontage; import org.signalml.domain.montage.SourceMontageEvent; import org.signalml.domain.montage.SourceMontageListener; import org.signalml.domain.montage.generators.IMontageGenerator; import org.springframework.validation.Errors; /** * The panel which allows to select the {@link MontageGenerator montage * generator} and generate a {@link Montage montage} using it. * The new montage is generated on the basis of the {@link #getMontage() * current montage}. * <p> * This panel contains three elements: * <ul> * <li>the {@link #getGeneratorComboBox() generator combo-box},</li> * <li>the {@link #getReloadButton() reload button},</li> * <li>the {@link #getShowErrorsButton() show errors button},</li></ul> * * @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o. */ public class MontageGeneratorPanel extends JPanel { /** the default serialization constant. */ private static final long serialVersionUID = 1L; /** the logger. */ protected static final Logger logger = Logger.getLogger(MontageGeneratorPanel.class); /** the {@link Montage montage} that is edited. */ private Montage montage; /** * the combo-box which allows to select the {@link MontageGenerator montage * genarator}. * When the generator is selected from the list it is used to {@link * #tryGenerate(MontageGenerator) generate} a new {@link Montage} on the * basis of the {@link #montage old one}. */ private JComboBox generatorComboBox; /** * the {@link MontageGeneratorListModel model} for the * {@link #generatorComboBox}. */ private MontageGeneratorListModel montageGeneratorListModel; /** * the dialog with errors which is shown when: * <ul> * <li>the {@link #getMontage() montage} can not be used to generate the. * {@link Montage montage} using a {@link #getGeneratorComboBox() selected} * {@link MontageGenerator generator},</li> * <li>when {@link #getShowErrorsAction() ShowErrorsAction} is performed,</li> * </ul> */ private ValidationErrorsDialog errorsDialog; /** * the {@link ShowErrorsAction action} which displays the. * errors dialog if the current {@link #getMontage() * montage} can not be used to generate the {@link Montage montage} using a * currently {@link #getGeneratorComboBox() selected} * {@link MontageGenerator generator} */ private ShowErrorsAction showErrorsAction; /** * the {@link ReloadAction action} which * {@link #tryGenerate(MontageGenerator) generates} a new {@link Montage} * using a currently {@link #getGeneratorComboBox() selected} * {@link MontageGenerator} (on the basis of the {@link #montage old * montage}). */ private ReloadAction reloadAction; /** the button for {@link #getShowErrorsAction() showErrorsAction}. */ private CompactButton showErrorsButton; /** the button for {@link #getReloadAction() reloadAction}. */ private CompactButton reloadButton; /** * the {@link MontagePropertyListener listener} associated with the change * of the {@link MontageGenerator montage generator} for the * {@link MontageGeneratorPanel#montage montage}. */ private MontagePropertyListener montagePropertyListener; /** * the listener associated with the addition/removal/change of a. * {@link SourceChannel source channel} in a {@link #getMontage() montage} */ private SourceMontageChangeListener sourceMontageChangeListener; /** * <code>true</code> if there is an ongoing event associated with the. * {@link #getGeneratorComboBox() generator combo-box}, which means there * will be no reaction on changes in the combo-box, <code>false</code> * otherwise */ private boolean lockComboEvents = false; /** * Constructor. Sets the source of messages and {@link #initialize() * initializes} this panel */ public MontageGeneratorPanel() { super(); initialize(); } /** * Initializes this panel with 3 elements: * <ul> * <li>the {@link #getGeneratorComboBox() generator combo-box},</li> * <li>the {@link #getReloadButton() reload button},</li> * <li>the {@link #getShowErrorsButton() show errors button},</li></ul> */ private void initialize() { montagePropertyListener = new MontagePropertyListener(); sourceMontageChangeListener = new SourceMontageChangeListener(); setLayout(new BorderLayout()); JPanel choicePanel = new JPanel(); choicePanel.setLayout(new BoxLayout(choicePanel, BoxLayout.X_AXIS)); CompoundBorder border = new CompoundBorder( new TitledBorder(_("Choose generator")), new EmptyBorder(3,3,3,3) ); choicePanel.setBorder(border); choicePanel.add(new JLabel(_("Generator"))); choicePanel.add(Box.createHorizontalStrut(5)); choicePanel.add(Box.createHorizontalGlue()); choicePanel.add(getGeneratorComboBox()); choicePanel.add(Box.createHorizontalStrut(10)); choicePanel.add(getReloadButton()); choicePanel.add(Box.createHorizontalStrut(5)); choicePanel.add(getShowErrorsButton()); add(choicePanel, BorderLayout.CENTER); } /** * Gets the {@link Montage montage} that is edited. * * @return the {@link Montage montage} that is edited */ public Montage getMontage() { return montage; } /** * Sets the {@link Montage montage} that is edited. * * @param montage * the new {@link Montage montage} that is edited */ public void setMontage(Montage montage) { if (this.montage != montage) { if (this.montage != null) { this.montage.removePropertyChangeListener(Montage.MONTAGE_GENERATOR_PROPERTY, montagePropertyListener); this.montage.removeSourceMontageListener(sourceMontageChangeListener); } this.montage = montage; IMontageGenerator generator = null; if (montage != null) { montage.addPropertyChangeListener(Montage.MONTAGE_GENERATOR_PROPERTY, montagePropertyListener); montage.addSourceMontageListener(sourceMontageChangeListener); getMontageGeneratorListModel().setEegSystem(montage.getEegSystem()); generator = montage.getMontageGenerator(); } else { getMontageGeneratorListModel().setEegSystem(null); } quietSetSelectedGenerator(generator); } } /** * Gets the validation dialog with errors which is shown when: * <ul> * <li>the {@link #getMontage() montage} can not be used to generate the. * * @return the {@link ValidationErrorsDialog} with errors which is shown when: * <ul> * <li>the {@link #getMontage() montage} can not be used to generate * the */ public ValidationErrorsDialog getErrorsDialog() { return errorsDialog; } /** * Sets the dialog with errors which is shown when: * <ul> * <li>the {@link #getMontage() montage} can not be used to generate the. * * @param errorsDialog * the new validation errors dialog with errors which is shown * when: * <ul> * <li>the {@link #getMontage() montage} can not be used to * generate the */ public void setErrorsDialog(ValidationErrorsDialog errorsDialog) { this.errorsDialog = errorsDialog; } /** * Gets the {@link MontageGeneratorListModel model} for the * {@link #generatorComboBox}. * * @return the {@link MontageGeneratorListModel model} for the * {@link #generatorComboBox} */ protected MontageGeneratorListModel getMontageGeneratorListModel() { if (montageGeneratorListModel == null) { montageGeneratorListModel = new MontageGeneratorListModel(); } return montageGeneratorListModel; } /** * Gets the combo-box which allows to select the {@link MontageGenerator * montage genarator}. * * @return the combo-box which allows to select the {@link MontageGenerator * montage genarator} */ public JComboBox getGeneratorComboBox() { if (generatorComboBox == null) { generatorComboBox = new ResolvableComboBox(); generatorComboBox.setModel(getMontageGeneratorListModel()); generatorComboBox.setPreferredSize(new Dimension(300,25)); generatorComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { if (lockComboEvents) { return; } Object item = getGeneratorComboBox().getSelectedItem(); if (montage == null) { return; } if (!(item instanceof IMontageGenerator)) { montage.setMontageGenerator(null); setEnableds(); return; } IMontageGenerator generator = (IMontageGenerator) item; setEnableds(); tryGenerate(generator); } }); } return generatorComboBox; } /** * Gets the {@link ShowErrorsAction action} which displays the. * * @return the {@link ShowErrorsAction action} which displays the */ public ShowErrorsAction getShowErrorsAction() { if (showErrorsAction == null) { showErrorsAction = new ShowErrorsAction(); } return showErrorsAction; } /** * Gets the {@link ReloadAction action} which * {@link #tryGenerate(MontageGenerator) generates} a new {@link Montage} * using a currently {@link #getGeneratorComboBox() selected} * {@link MontageGenerator} (on the basis of the {@link #montage old * montage}). * * @return the {@link ReloadAction action} which * {@link #tryGenerate(MontageGenerator) generates} a new * {@link Montage} using a currently {@link #getGeneratorComboBox() * selected} {@link MontageGenerator} (on the basis of the * {@link #montage old montage}) */ public ReloadAction getReloadAction() { if (reloadAction == null) { reloadAction = new ReloadAction(); } return reloadAction; } /** * Gets the button for {@link #getShowErrorsAction() showErrorsAction}. * * @return the button for {@link #getShowErrorsAction() showErrorsAction} */ public CompactButton getShowErrorsButton() { if (showErrorsButton == null) { showErrorsButton = new CompactButton(getShowErrorsAction()); } return showErrorsButton; } /** * Gets the button for {@link #getReloadAction() reloadAction}. * * @return the button for {@link #getReloadAction() reloadAction} */ public CompactButton getReloadButton() { if (reloadButton == null) { reloadButton = new CompactButton(getReloadAction()); } return reloadButton; } /** * Selects the provided {@link MontageGenerator montage generator} in the * {@link #getGeneratorComboBox() generator combo-box}. * While doing that {@link #lockComboEvents suppresses} all the events * associated with this combo-box. * @param generator the {@link MontageGenerator montage generator} to be * selected */ private void quietSetSelectedGenerator(IMontageGenerator generator) { try { lockComboEvents = true; if (generator == null) { getGeneratorComboBox().setSelectedIndex(0); } else { getGeneratorComboBox().setSelectedItem(generator); } } finally { lockComboEvents = false; } getGeneratorComboBox().repaint(); setEnableds(); } /** * Tries to generate a new {@link Montage montage} on the basis of the * current {@link #getMontage() montage} using the provided {@link * MontageGenerator montage generator}: * <ol> * <li>{@link MontageGenerator#validateSourceMontage(SourceMontage, Errors) * Checks} if the current montage can be used with the provided generator. * If not shows the errors.</li> * <li>{@link MontageGenerator#createMontage(Montage) creates} the montage * on the basis of the current montage,</li> * </ol> * @param generator the {@link MontageGenerator montage generator} to be * used */ public void tryGenerate(IMontageGenerator generator) { ValidationErrors errors = new ValidationErrors(); generator.validateSourceMontage(montage, errors); if (errors.hasErrors()) { errorsDialog.showDialog(errors); if (montage.getMontageGenerator() != null) montageGeneratorListModel.setSelectedItem(montage.getMontageGenerator()); else montageGeneratorListModel.setSelectedItem(MontageGeneratorListModel.NO_GENERATOR); return; } try { generator.createMontage(montage); } catch (MontageException ex) { logger.error("Montage generation failed", ex); Dialogs.showExceptionDialog(this, ex); quietSetSelectedGenerator(null); return; } } /** * Enables or disables {@link #getShowErrorsAction() show errors} and * {@link #getReloadAction() reload} button. * If there are errors during the * {@link MontageGenerator#validateSourceMontage(SourceMontage, Errors) * validation} of the {@link #getMontage() montage} show errors button is * enabled and reload button is disabled. If there were no errors the * situation is symmetrical. */ public void setEnableds() { Object item = getGeneratorComboBox().getSelectedItem(); if (!(item instanceof IMontageGenerator)) { getShowErrorsAction().setEnabled(false); getReloadAction().setEnabled(false); return; } IMontageGenerator generator = (IMontageGenerator) item; if (montage != null) { ValidationErrors errors = new ValidationErrors(); generator.validateSourceMontage(montage, errors); boolean hasErrors = errors.hasErrors(); getShowErrorsAction().setEnabled(hasErrors); getReloadAction().setEnabled(!hasErrors); } } /** * Action which displays an error dialog if the current {@link #getMontage() * montage} can not be used to generate a {@link Montage montage} using a * currently {@link #getGeneratorComboBox() selected} * {@link MontageGenerator generator}. */ protected class ShowErrorsAction extends AbstractAction { /** the default serialization constant. */ private static final long serialVersionUID = 1L; /** * Constructor. Creates a new action and sets a label * and an icon for the button associated with this action. */ public ShowErrorsAction() { putValue(AbstractAction.SMALL_ICON, IconUtils.loadClassPathIcon("org/signalml/app/icon/errormedium.png")); putValue(AbstractAction.SHORT_DESCRIPTION, _("Click to see errors")); } /** * When the action is performed shows * the errors that appeared during the {@link * MontageGenerator#validateSourceMontage(SourceMontage, Errors) * validation} of the {@link MontageGeneratorPanel#getGeneratorComboBox() * selected} {@link Montage}. */ @Override public void actionPerformed(ActionEvent ev) { Object item = getGeneratorComboBox().getSelectedItem(); if (!(item instanceof IMontageGenerator)) { return; } IMontageGenerator generator = (IMontageGenerator) item; ValidationErrors errors = new ValidationErrors(); generator.validateSourceMontage(montage, errors); if (errors.hasErrors()) { errorsDialog.showDialog(errors); } } } /** * Action which {@link MontageGeneratorPanel#tryGenerate(MontageGenerator) * generates} a new {@link Montage} using a currently {@link * MontageGeneratorPanel#getGeneratorComboBox() selected} {@link * MontageGenerator} (on the basis of the {@link * MontageGeneratorPanel#getMontage() old montage}). */ protected class ReloadAction extends AbstractAction { /** the default serialization constant. */ private static final long serialVersionUID = 1L; /** * Constructor. Creates a new action and sets a label * and an icon for the button associated with this action. */ public ReloadAction() { putValue(AbstractAction.SMALL_ICON, IconUtils.loadClassPathIcon("org/signalml/app/icon/reloadmedium.png")); putValue(AbstractAction.SHORT_DESCRIPTION, _("Generate again")); } /** * When the action is performed {@link * MontageGeneratorPanel#tryGenerate(MontageGenerator) generates} * a new {@link Montage} using a {@link * MontageGeneratorPanel#getGeneratorComboBox() selected} * {@link MontageGenerator generator}. */ @Override public void actionPerformed(ActionEvent ev) { Object item = getGeneratorComboBox().getSelectedItem(); if (!(item instanceof IMontageGenerator)) { return; } IMontageGenerator generator = (IMontageGenerator) item; tryGenerate(generator); } } /** * The listener associated with the change of the {@link MontageGenerator * montage generator} for the {@link MontageGeneratorPanel#montage montage}. * <p> * When the event occurs: * <ul> * <li>updates the state of the * {@link MontageGeneratorPanel#getGeneratorComboBox() generatorComboBox},</li> * <li>updates the state of the * {@link MontageGeneratorPanel#getReloadAction() reload} and * {@link MontageGeneratorPanel#getShowErrorsAction() show errors} actions,</li> * <li>{@link MontageGenerator#validateSourceMontage(SourceMontage, Errors) * validates} the {@link Montage montage}.</li> * </ul> */ protected class MontagePropertyListener implements PropertyChangeListener { /** * When the property is changed: * <ul> * <li>updates the state of the {@link * MontageGeneratorPanel#getGeneratorComboBox() generatorComboBox},</li> * <li>updates the state of the {@link * MontageGeneratorPanel#getReloadAction() reload} and {@link * MontageGeneratorPanel#getShowErrorsAction() show errors} actions,</li> * <li>{@link MontageGenerator#validateSourceMontage(SourceMontage, Errors) * validates} the {@link Montage montage}.</li></ul> */ @Override public void propertyChange(PropertyChangeEvent evt) { quietSetSelectedGenerator((IMontageGenerator) evt.getNewValue()); } } /** * The listener associated with the addition/removal/change of a. * * {@link SourceChannel source channel} in a montage. * <p> * When any of these events occurs: * <ul> * <li>updates the state of the * {@link MontageGeneratorPanel#getReloadAction() reload} and * {@link MontageGeneratorPanel#getShowErrorsAction() show errors} actions,</li> * <li>{@link MontageGenerator#validateSourceMontage(SourceMontage, Errors) * validates} the {@link Montage montage}.</li> * </ul> */ protected class SourceMontageChangeListener implements SourceMontageListener { /** * <ul> * <li>Updates the state of the {@link * MontageGeneratorPanel#getReloadAction() reload} and {@link * MontageGeneratorPanel#getShowErrorsAction() show errors} actions,</li> * <li>{@link MontageGenerator#validateSourceMontage(SourceMontage, Errors) * validates} the {@link Montage montage}.</li></ul> */ private void onChange() { if (getGeneratorComboBox().getSelectedItem() instanceof IMontageGenerator) { setEnableds(); } } /** * @see #onChange() */ @Override public void sourceMontageChannelAdded(SourceMontageEvent ev) { onChange(); } /** * @see #onChange() */ @Override public void sourceMontageChannelChanged(SourceMontageEvent ev) { onChange(); } /** * @see #onChange() */ @Override public void sourceMontageChannelRemoved(SourceMontageEvent ev) { onChange(); } @Override public void sourceMontageEegSystemChanged(SourceMontageEvent ev) { getMontageGeneratorListModel().setEegSystem(montage.getEegSystem()); } } }