All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.mvc.pom.view.PomProgrammerView Maven / Gradle / Ivy

There is a newer version: 2.0.0-M1
Show newest version
package org.bidib.wizard.mvc.pom.view;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.WindowConstants;

import org.bidib.jbidibc.core.AddressData;
import org.bidib.jbidibc.core.enumeration.CommandStationState;
import org.bidib.jbidibc.core.enumeration.PomOperation;
import org.bidib.jbidibc.core.enumeration.PomProgState;
import org.bidib.wizard.locale.Resources;
import org.bidib.wizard.mvc.common.view.ComponentUtils;
import org.bidib.wizard.mvc.common.view.converter.StringConverter;
import org.bidib.wizard.mvc.common.view.panel.DisabledPanel;
import org.bidib.wizard.mvc.common.view.validation.IconFeedbackPanel;
import org.bidib.wizard.mvc.common.view.validation.PropertyValidationI18NSupport;
import org.bidib.wizard.mvc.pom.model.PomProgrammerModel;
import org.bidib.wizard.mvc.pom.view.listener.PomProgrammerViewListener;
import org.bidib.wizard.mvc.pom.view.panel.AddressPanel;
import org.bidib.wizard.mvc.pom.view.panel.DirectAccessPanel;
import org.bidib.wizard.mvc.pom.view.panel.LogAreaAware;
import org.bidib.wizard.mvc.pom.view.panel.PomValidationResultModel;
import org.bidib.wizard.mvc.pom.view.panel.RailcomPanel;
import org.bidib.wizard.mvc.pom.view.panel.listener.PomRequestListener;
import org.bidib.wizard.mvc.pom.view.panel.listener.PomResultListener;
import org.bidib.wizard.utils.InputValidationDocument;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jgoodies.binding.adapter.Bindings;
import com.jgoodies.binding.beans.PropertyAdapter;
import com.jgoodies.binding.beans.PropertyConnector;
import com.jgoodies.binding.value.ConverterValueModel;
import com.jgoodies.binding.value.ValueModel;
import com.jgoodies.forms.builder.ButtonBarBuilder;
import com.jgoodies.forms.builder.DefaultFormBuilder;
import com.jgoodies.forms.debug.FormDebugPanel;
import com.jgoodies.forms.factories.Borders;
import com.jgoodies.forms.layout.FormLayout;
import com.jgoodies.validation.Severity;
import com.jgoodies.validation.ValidationResult;
import com.jgoodies.validation.ValidationResultModel;
import com.jgoodies.validation.util.PropertyValidationSupport;
import com.jgoodies.validation.view.ValidationComponentUtils;
import com.vlsolutions.swing.docking.DockKey;
import com.vlsolutions.swing.docking.Dockable;
import com.vlsolutions.swing.docking.DockingDesktop;

public class PomProgrammerView implements Dockable {
    private static final Logger LOGGER = LoggerFactory.getLogger(PomProgrammerView.class);

    private final DockKey DOCKKEY = new DockKey("PomProgrammerView");

    private final Collection listeners = new LinkedList();

    private final PomProgrammerModel cvProgrammerModel;

    private Timer switchToRunningModeController;

    private final JButton clearButton = new JButton(Resources.getString(getClass(), "clearLogArea"));

    private final JButton closeButton = new JButton(Resources.getString(getClass(), "close"));

    private static final String ENCODED_DIALOG_COLUMN_SPECS = "pref, 3dlu, fill:50dlu:grow";

    private DirectAccessPanel directAccessPanel;

    private AddressPanel addressPanel;

    private RailcomPanel railcomPanel;

    private List pomResultListeners = new LinkedList();

    private final JTabbedPane tabbedPane;

    private final JPanel contentPanel;

    private static final String ENCODED_LOCAL_COLUMN_SPECS =
        "max(50dlu;pref), 3dlu, max(50dlu;pref), 3dlu, pref, 3dlu, pref:grow";

    private ValueModel currentAddressValueModel;

    private JTextField currentAddress;

    private InputValidationDocument currentAddressDocument;

    private CurrentAddressBeanModel currentAddressBeanModel;

    private PomValidationResultModel currentAddressValidationModel;

    /**
     * Creates a new instance of PomProgrammerView.
     * 
     * @param pomProgrammerModel
     *            the POM programmer model
     */
    public PomProgrammerView(final PomProgrammerModel pomProgrammerModel) {
        this.cvProgrammerModel = pomProgrammerModel;

        DOCKKEY.setName(Resources.getString(getClass(), "title"));
        // turn off autohide and close features
        DOCKKEY.setFloatEnabled(true);
        DOCKKEY.setAutoHideEnabled(false);

        currentAddressValidationModel = new PomValidationResultModel();
        currentAddressBeanModel = new CurrentAddressBeanModel();

        // create the tabbed pane for the special POM operations
        tabbedPane = new JTabbedPane();

        PomRequestListener pomRequestListener = new PomRequestListener() {
            @Override
            public void sendRequest(
                PomResultListener pomResultListener, AddressData decoderAddress, PomOperation operation, int cvNumber,
                int cvValue) {
                LOGGER.info("Send request, pomResultListener: {}", pomResultListener);

                // TODO not sure if this is correct here
                cvProgrammerModel.setPomProgState(PomProgState.POM_PROG_START);

                for (PomResultListener resultListener : pomResultListeners) {
                    resultListener.setActive(resultListener.equals(pomResultListener));
                }

                // disable the other tabs
                int selectedIndex = tabbedPane.getSelectedIndex();
                LOGGER.info("Disable the unselected tabs, selectedIndex: {}", selectedIndex);
                for (int index = 0; index < tabbedPane.getTabCount(); index++) {
                    tabbedPane.setEnabledAt(index, index == selectedIndex);
                }

                // send the request
                for (PomProgrammerViewListener l : listeners) {
                    l.sendRequest(decoderAddress, operation, cvNumber, cvValue);
                }
            }
        };

        directAccessPanel = new DirectAccessPanel(cvProgrammerModel, currentAddressBeanModel);
        directAccessPanel.addPomRequestListener(pomRequestListener);
        pomResultListeners.add(directAccessPanel);

        addressPanel = new AddressPanel(cvProgrammerModel, currentAddressBeanModel);
        addressPanel.addPomRequestListener(pomRequestListener);
        pomResultListeners.add(addressPanel);

        railcomPanel = new RailcomPanel(cvProgrammerModel, currentAddressBeanModel);
        railcomPanel.addPomRequestListener(pomRequestListener);
        pomResultListeners.add(railcomPanel);

        closeButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                close();
            }
        });

        clearButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                // clear the logger area of the selected tab
                Component comp = tabbedPane.getSelectedComponent();
                LogAreaAware logAreaAware = ComponentUtils.harvestComponent((Container) comp, LogAreaAware.class);
                LOGGER.info("Found logAreaAware: {}", logAreaAware);
                if (logAreaAware != null) {
                    logAreaAware.clearLogArea();
                }
            }
        });

        // prepare the close button
        JPanel buttons = new ButtonBarBuilder().addButton(clearButton).addGlue().addButton(closeButton).build();

        DefaultFormBuilder dialogBuilder = null;
        boolean debugDialog = false;
        if (debugDialog) {
            JPanel panel = new FormDebugPanel();
            dialogBuilder = new DefaultFormBuilder(new FormLayout(ENCODED_DIALOG_COLUMN_SPECS), panel);
        }
        else {
            JPanel panel = new JPanel(new BorderLayout());
            dialogBuilder = new DefaultFormBuilder(new FormLayout(ENCODED_DIALOG_COLUMN_SPECS), panel);
        }
        dialogBuilder.border(Borders.DIALOG);

        // create the current address content

        currentAddressValueModel =
            new PropertyAdapter(currentAddressBeanModel,
                CurrentAddressBeanModel.PROPERTYNAME_ADDRESS, true);

        final ValueModel addressConverterModel =
            new ConverterValueModel(currentAddressValueModel, new StringConverter(new DecimalFormat("#")));

        // create a panel with feedback for the current address only and add this panel to the dialog
        DefaultFormBuilder localBuilder = new DefaultFormBuilder(new FormLayout(ENCODED_LOCAL_COLUMN_SPECS));

        // create the textfield for the CV number
        currentAddress = new JTextField();
        currentAddressDocument = new InputValidationDocument(5, InputValidationDocument.NUMERIC);
        currentAddress.setDocument(currentAddressDocument);
        // currentAddress.setColumns(3);

        // bind manually because we changed the document of the textfield
        Bindings.bind(currentAddress, addressConverterModel, false);
        localBuilder.append(Resources.getString(getClass(), "current-address"), currentAddress);

        // show / hide
        final JButton addressChangedAcknButton = new JButton(Resources.getString(getClass(), "ackn-address-changed"));
        localBuilder.append(addressChangedAcknButton);
        addressChangedAcknButton.setVisible(false);
        PropertyConnector.connect(currentAddressBeanModel, CurrentAddressBeanModel.PROPERTYNAME_ADDRESS_CHANGED,
            addressChangedAcknButton, "visible");

        ValidationComponentUtils.setMandatory(currentAddress, true);
        ValidationComponentUtils.setMessageKeys(currentAddress, "validation.current_address_key");

        // check if we have validation enabled
        if (getValidationResultModel() != null) {
            LOGGER.info("Create iconfeedback panel.");
            JComponent cvIconPanel = new IconFeedbackPanel(getValidationResultModel(), localBuilder.build());
            DefaultFormBuilder feedbackBuilder = null;
            feedbackBuilder = new DefaultFormBuilder(new FormLayout("p:g"));

            feedbackBuilder.appendRow("fill:max(20dlu;p):grow");
            feedbackBuilder.add(cvIconPanel);

            JPanel panel = feedbackBuilder.build();
            dialogBuilder.append(panel, 3);
            // triggerValidation();
        }
        else {
            dialogBuilder.append(localBuilder.build(), 3);
        }

        // add tabs
        tabbedPane.addTab(Resources.getString(getClass(), "tab-address"), null/* icon */,
            addressPanel.createPanel(currentAddressValidationModel),
            Resources.getString(getClass(), "tab-address.tooltip"));
        tabbedPane.setMnemonicAt(0, KeyEvent.VK_1);

        tabbedPane.addTab(Resources.getString(getClass(), "tab-railcom"), null/* icon */,
            railcomPanel.createPanel(currentAddressValidationModel),
            Resources.getString(getClass(), "tab-railcom.tooltip"));
        tabbedPane.setMnemonicAt(1, KeyEvent.VK_2);

        tabbedPane.addTab(Resources.getString(getClass(), "tab-direct-access"), null/* icon */,
            directAccessPanel.createPanel(currentAddressValidationModel),
            Resources.getString(getClass(), "tab-direct-access.tooltip"));
        tabbedPane.setMnemonicAt(2, KeyEvent.VK_3);

        dialogBuilder.appendRow("3dlu");

        dialogBuilder.appendRow("fill:p:grow");
        dialogBuilder.nextLine(2);
        dialogBuilder.append(tabbedPane, 3);
        dialogBuilder.nextLine();

        dialogBuilder.appendRow("3dlu");
        dialogBuilder.nextLine();

        dialogBuilder.appendRow("p");
        dialogBuilder.append(buttons, 3);

        contentPanel = dialogBuilder.build();

        DisabledPanel.disable(contentPanel);
        closeButton.setEnabled(true);

        cvProgrammerModel.addPropertyChangeListener(PomProgrammerModel.PROPERTYNAME_COMMANDSTATIONSTATE,
            new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {

                    final CommandStationState commandStationState = (CommandStationState) evt.getNewValue();
                    LOGGER.info("The commandStationState has changed: {}", commandStationState);
                    if (SwingUtilities.isEventDispatchThread()) {
                        signalCommandStationStateChanged(commandStationState);
                    }
                    else {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                signalCommandStationStateChanged(commandStationState);
                            }
                        });
                    }
                }
            });

        cvProgrammerModel.addPropertyChangeListener(PomProgrammerModel.PROPERTYNAME_POMPROGSTATE,
            new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {

                    final PomProgState pomProgState = (PomProgState) evt.getNewValue();
                    LOGGER.info("The pomProgState has changed: {}", pomProgState);
                    if (SwingUtilities.isEventDispatchThread()) {
                        signalPomProgStateChanged(pomProgState);
                    }
                    else {
                        SwingUtilities.invokeLater(new Runnable() {
                            @Override
                            public void run() {
                                signalPomProgStateChanged(pomProgState);
                            }
                        });
                    }
                }
            });

        currentAddressBeanModel.addPropertyChangeListener(CurrentAddressBeanModel.PROPERTYNAME_ADDRESS,
            new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    LOGGER.debug("Address has changed: {}", currentAddressBeanModel.getAddress());
                    triggerValidation();
                }
            });
        currentAddressBeanModel.addPropertyChangeListener(CurrentAddressBeanModel.PROPERTYNAME_ADDRESS_CHANGED,
            new PropertyChangeListener() {

                @Override
                public void propertyChange(PropertyChangeEvent evt) {
                    LOGGER.debug("Address change was signalled: {}", currentAddressBeanModel.getAddressChanged());
                    triggerValidation();
                }
            });

        addressChangedAcknButton.addActionListener(new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {
                // reset the flag
                currentAddressBeanModel.setAddressChanged(Boolean.FALSE);
            }
        });
        // trigger the initial validation
        triggerValidation();
    }

    @Override
    public Component getComponent() {
        return contentPanel;
    }

    @Override
    public DockKey getDockKey() {
        return DOCKKEY;
    }

    private void triggerValidation() {
        LOGGER.info("Trigger the validation.");
        ValidationResult validationResult = validate();
        currentAddressValidationModel.setResult(validationResult);
    }

    private static final int MIN_ADDRESS = 1;

    private static final int MAX_ADDRESS = 10239;

    private ValidationResult validate() {
        PropertyValidationSupport support = new PropertyValidationI18NSupport(currentAddressBeanModel, "validation");

        // only addresses between 1 and 10239 are valid
        if (currentAddressBeanModel.getAddress() == null) {
            support.addError("current_address_key", "not_empty");
        }
        else if (currentAddressBeanModel.getAddress().intValue() < MIN_ADDRESS
            || currentAddressBeanModel.getAddress().intValue() > MAX_ADDRESS) {
            support.addError("current_address_key", "invalid_value;min=" + MIN_ADDRESS + ",max=" + MAX_ADDRESS);
        }

        if (Boolean.TRUE.equals(currentAddressBeanModel.getAddressChanged())) {
            support.add(Severity.WARNING, "current_address_key", "address_changed_automatically");
        }
        ValidationResult validationResult = support.getResult();
        LOGGER.info("Prepared validationResult: {}", validationResult);
        return validationResult;
    }

    private ValidationResultModel getValidationResultModel() {
        return currentAddressValidationModel;
    }

    public void prepareDockable(DockingDesktop desktop, int x, int y) {

        desktop.addDockable(this);
        // desktop.setFloating(this, true);
        // switch to programming mode ...
        initialize();
    }

    private JDialog dialog;

    public void showDialog(JFrame parent, int x, int y) {

        dialog = new JDialog(parent, false);

        // dialog.setResizable(false);
        dialog.setTitle(Resources.getString(getClass(), "title"));
        dialog.setLayout(new BorderLayout());
        dialog.setContentPane(contentPanel);

        dialog.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                close();
            }
        });
        dialog.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        dialog.pack();

        dialog.setMinimumSize(new Dimension((int) contentPanel.getPreferredSize().getWidth() + 10, (int) contentPanel
            .getPreferredSize().getHeight() + 35));

        dialog.setLocation(x, y);

        dialog.setVisible(true);

        // perform some initialization ...
        initialize();
    }

    public void initialize() {
        LOGGER.info("Switch CS to running mode.");

        switchToRunningModeController = new Timer(5000, new ActionListener() {

            @Override
            public void actionPerformed(ActionEvent e) {

                switchToRunningModeController.stop();

                LOGGER.warn("Switch CommandStation to running mode was not established in 5 seconds!");
                JOptionPane.showMessageDialog(contentPanel,
                    Resources.getString(PomProgrammerView.class, "switch-to-running-mode-failed.message"),
                    Resources.getString(PomProgrammerView.class, "switch-to-running-mode-failed.title"),
                    JOptionPane.ERROR_MESSAGE);

                // close the programming dialog
                close();
            }
        });
        switchToRunningModeController.setRepeats(false);

        //
        LOGGER.info("Switch the command station on.");
        addLogText("Switch the command station on.");

        try {
            boolean activateControlTimer = fireSetRunningMode(true);

            if (activateControlTimer) {
                LOGGER.info("Start control timer for switch to running mode is started.");
                if (switchToRunningModeController != null) {
                    switchToRunningModeController.start();
                }
                else {
                    LOGGER.warn("The control timer is no longer needed!");
                }
            }
            else {
                LOGGER.info("Start control timer for switch to running mode is NOT started.");
            }
        }
        catch (IllegalArgumentException ex) {
            LOGGER.error("Switch command station to running state failed.");

            JOptionPane.showMessageDialog(contentPanel,
                Resources.getString(PomProgrammerView.class, "switch-to-running-mode-failed.message"),
                Resources.getString(PomProgrammerView.class, "switch-to-running-mode-failed.title"),
                JOptionPane.ERROR_MESSAGE);

            // close the programming dialog
            close();
        }
    }

    public void addPomProgrammerViewListener(PomProgrammerViewListener l) {
        listeners.add(l);
    }

    private void addLogText(final String logLine, Object... args) {
        for (PomResultListener listener : pomResultListeners) {
            listener.addLogText(logLine, args);
        }
    }

    private void signalCommandStationStateChanged(CommandStationState commandStationState) {
        if (CommandStationState.GO.equals(commandStationState)
            || CommandStationState.GO_IGN_WD.equals(commandStationState)) {
            if (switchToRunningModeController != null) {
                LOGGER.info("The command station has switched to running mode. Stop the control timer.");
                switchToRunningModeController.stop();
                switchToRunningModeController = null;

                addLogText("Switched to programming mode passed.");

                DisabledPanel.enable(contentPanel);
            }
            else {
                LOGGER.info("No control timer available.");
            }
        }
    }

    private void signalPomProgStateChanged(PomProgState pomProgState) {

        // enable the tabs before the result is passed to the listeners
        switch (pomProgState) {
            case POM_PROG_START:
            case POM_PROG_RUNNING:
                break;
            default:
                // enable all tabs
                LOGGER.info("Enable the tabs");
                for (int index = 0; index < tabbedPane.getTabCount(); index++) {
                    tabbedPane.setEnabledAt(index, true);
                }
                break;
        }

        for (PomResultListener listener : pomResultListeners) {
            listener.signalPomProgStateChanged(pomProgState);
        }
    }

    private void close() {
        contentPanel.setVisible(false);

        if (switchToRunningModeController != null) {
            LOGGER.info("Stop the control timer for the command station has switched to running mode.");
            switchToRunningModeController.stop();
            switchToRunningModeController = null;
        }

        // LOGGER.info("Terminate the programming mode!");
        // fireSetProgrammingMode(false);

        LOGGER.info("Close the dialog.");
        fireClose();

        if (dialog != null) {
            dialog.dispose();

            dialog = null;
        }
    }

    private void fireClose() {
        for (PomProgrammerViewListener l : listeners) {
            l.close();
        }
    }

    private boolean fireSetRunningMode(boolean activateProgMode) {
        boolean startControlTimer = false;
        for (PomProgrammerViewListener l : listeners) {
            LOGGER.info("+++ Send the command station state request: {}", activateProgMode);
            boolean messageSent = l.sendCommandStationStateRequest(activateProgMode);
            if (!startControlTimer) {
                // set once to true ...
                startControlTimer = messageSent;
            }
        }
        return startControlTimer;
    }

    public void closeDialog() {
        LOGGER.info("Close the dialog is requested.");

        close();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy