JMadGui.java

// @formatter:off
 /*******************************************************************************
 *
 * This file is part of JMad.
 * 
 * Copyright (c) 2008-2011, CERN. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 ******************************************************************************/
// @formatter:on

package cern.accsoft.steering.jmad.gui;

import static java.util.Objects.requireNonNull;
import static javax.swing.SwingUtilities.invokeLater;

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

import cern.accsoft.steering.jmad.domain.ex.JMadModelException;
import cern.accsoft.steering.jmad.domain.machine.Range;
import cern.accsoft.steering.jmad.gui.actions.event.ChooseOpticsEvent;
import cern.accsoft.steering.jmad.gui.actions.event.ChooseRangeEvent;
import cern.accsoft.steering.jmad.gui.actions.event.CloseActiveModelEvent;
import cern.accsoft.steering.jmad.gui.actions.event.CreateModelFromFileEvent;
import cern.accsoft.steering.jmad.gui.actions.event.CreateModelFromRepositoryEvent;
import cern.accsoft.steering.jmad.gui.actions.event.CreateModelFromUriEvent;
import cern.accsoft.steering.jmad.gui.actions.event.ExitEvent;
import cern.accsoft.steering.jmad.gui.actions.event.ExportModelEvent;
import cern.accsoft.steering.jmad.gui.actions.event.ExportModelUriEvent;
import cern.accsoft.steering.jmad.gui.actions.event.ShowAboutBoxEvent;
import cern.accsoft.steering.jmad.gui.dialog.AboutDialog;
import cern.accsoft.steering.jmad.gui.dialog.JMadOptionPane;
import cern.accsoft.steering.jmad.gui.executor.AsyncExecutor;
import cern.accsoft.steering.jmad.gui.icons.Icon;
import cern.accsoft.steering.jmad.gui.manage.JMadGuiPreferences;
import cern.accsoft.steering.jmad.gui.panels.GuiLogPanel;
import cern.accsoft.steering.jmad.gui.panels.ModelOpticsSelectionPanel;
import cern.accsoft.steering.jmad.gui.panels.RangeSelectionPanel;
import cern.accsoft.steering.jmad.model.AbstractJMadModelListener;
import cern.accsoft.steering.jmad.model.JMadModel;
import cern.accsoft.steering.jmad.model.manage.JMadModelManager;
import cern.accsoft.steering.jmad.model.manage.JMadModelManagerAdapter;
import cern.accsoft.steering.jmad.service.JMadService;
import cern.accsoft.steering.util.gui.UserInteractor;
import org.jmad.modelpack.gui.conf.JMadModelSelectionDialogFactory;
import org.jmad.modelpack.service.JMadModelPackageService;
import org.jmad.modelpack.util.ModelUris;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;

/**
 * this class represents the main frame for the jmad-gui standalone application
 *
 * @author Kajetan Fuchsberger (kajetan.fuchsberger at cern.ch)
 */
public class JMadGui extends JFrame {
    private static final long serialVersionUID = 1L;

    private static final Logger LOGGER = LoggerFactory.getLogger(JMadGui.class);
    private static final String TITLE_BASE = "JMad ";
    private static final int DEFAULT_WIDTH = 1024;
    private static final int DEFAULT_HEIGHT = 768;

    private final JMadGuiPreferences jmadGuiPreferences;
    private AsyncExecutor asyncExecutor;
    private JMadService jMadService;
    private JMadModelSelectionDialogFactory jMadModelSelectionDialogFactory;
    private JMadModelPackageService jMadModelPackageService;
    private JMadModelManager modelManager;
    private UserInteractor userInteractor;
    private RangeSelectionPanel rangeSelectionPanel;
    private ModelOpticsSelectionPanel modelOpticsSelectionPanel;
    private JPanel mainPanel;
    private JMenuBar jmadMenuBar;
    private JToolBar toolBar;
    private GuiLogPanel guiLogPanel;

    public JMadGui(JMadGuiPreferences jmadGuiPreferences) {
        this.jmadGuiPreferences = jmadGuiPreferences;
    }

    public final void init() {
        requireNonNull(mainPanel, "mainPanel cannot be null. Configuration problem");
        setupFramePreferences();

        setLayout(new BorderLayout());
        setIconImage(Icon.JMAD.getImageIcon().getImage());
        setContentPane(mainPanel);

        if (jmadMenuBar != null) {
            setJMenuBar(jmadMenuBar);
        }
        if (toolBar != null) {
            add(toolBar, BorderLayout.PAGE_START);
        }

        if (guiLogPanel != null) {
            add(guiLogPanel, BorderLayout.PAGE_END);
        }

        setPreferredSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT));
    }

    @Override
    public void validate() {
        super.validate();
        setPreferredSize(getSize());
    }

    public void showGui() {
        invokeLater(() -> {
            pack();
            setVisible(true);
        });
    }

    private void setupFramePreferences() {
        jmadGuiPreferences.exitOnCloseProperty().subscribe(close -> invokeLater(() -> setFrameCloseOperation(close)));
        setFrameCloseOperation(jmadGuiPreferences.isExitOnClose());

        addWindowListener(new WindowAdapter() {
            public void windowClosing(@SuppressWarnings("unused") WindowEvent e) {
                if (jmadGuiPreferences.isCleanupOnClose()) {
                    cleanup();
                }
                if (jmadGuiPreferences.isExitOnClose()) {
                    System.exit(0);
                }
            }
        });
    }

    private void cleanup() {
        if (this.modelManager != null) {
            JMadModel model = this.modelManager.getActiveModel();
            if (model != null) {
                try {
                    model.cleanup();
                } catch (JMadModelException e) {
                    LOGGER.error("Error while cleaning up model", e);
                }
            }
        }
    }

    private void updateTitleAccordingTo(JMadModel model) {
        if (model != null) {
            model.addListener(new AbstractJMadModelListener() {
                @Override
                public void opticsDefinitionChanged() {
                    updateTitle();
                }

                @Override
                public void rangeChanged(@SuppressWarnings("unused") Range newRange) {
                    updateTitle();
                }
            });
        }
        updateTitle();
    }

    private void updateTitle() {
        if (this.modelManager == null) {
            return;
        }

        JMadModel model = this.modelManager.getActiveModel();

        if (model == null) {
            setTitle(TITLE_BASE);
        } else {
            setTitle(TITLE_BASE + model.getDescription());
        }
    }

    private void showExportModelDefinitionDialog() {
        JMadModel model = modelManager.getActiveModel();
        if (model == null) {
            LOGGER.warn("No active model to export!");
            return;
        }
        JMadOptionPane.showExportModelDefinitionDialog(this, model.getModelDefinition(), jMadService);
    }

    private void showExportModelUriDialog() {
        JMadModel model = modelManager.getActiveModel();
        if (model == null) {
            LOGGER.warn("No active model to export!");
            return;
        }
        try {
            String modelUri = ModelUris.modelUri(model).toASCIIString();
            LOGGER.info("Model URI: {}", modelUri);
            JOptionPane.showMessageDialog(this, new JTextField(modelUri));
        } catch (Exception e) {
            LOGGER.error("Error getting model URI: {}", e.getMessage(), e);
        }
    }

    private void showRangeDefinitionChooseDialog() {
        userInteractor.showPanelDialog(rangeSelectionPanel, this);
    }

    private void showOpticsDefinitionChooseDialog() {
        userInteractor.showPanelDialog(modelOpticsSelectionPanel, this);
    }

    private void showCreateModelFromRepositoryDialog() {
        initializeModel(JMadOptionPane.showCreateModelDialog(jMadModelSelectionDialogFactory, jMadService));
    }

    private void showCreateModelFromFileDialog() {
        initializeModel(JMadOptionPane.showCreateModelFromFileDialog(this, jMadService));
    }

    private void showCreateModelFromUriDialog() {
        initializeModel(JMadOptionPane.showCreateModelFromUriDialog(this, jMadModelPackageService));
    }

    private void initializeModel(JMadModel model) {
        if (model == null) {
            return;
        }
        asyncExecutor.submitAsync("Initializing model '" + model.getName() + "'", () -> {
            LOGGER.info("Starting Initialization of model '{}'", model.getName());
            try {
                model.reset();
                LOGGER.info("Initialization of model '{}' finished.", model.getName());
            } catch (JMadModelException e) {
                LOGGER.error("Error while initializing Model '{}'.", model.getName(), e);
            }
        });
    }

    @EventListener(CreateModelFromRepositoryEvent.class)
    public void createModelFromRepositoryEventListener() {
        invokeLater(this::showCreateModelFromRepositoryDialog);
    }

    @EventListener(CreateModelFromUriEvent.class)
    public void createModelFromUriEventListener() {
        invokeLater(this::showCreateModelFromUriDialog);
    }

    @EventListener(CreateModelFromFileEvent.class)
    public void createModelFromFileEventListener() {
        invokeLater(this::showCreateModelFromFileDialog);
    }

    @EventListener(ExportModelEvent.class)
    public void exportModelEventListener() {
        invokeLater(this::showExportModelDefinitionDialog);
    }

    @EventListener(ExportModelUriEvent.class)
    public void exportModelUriEventListener() {
        invokeLater(this::showExportModelUriDialog);
    }

    @EventListener(CloseActiveModelEvent.class)
    public void closeActiveModelEventListener() {
        invokeLater(this::closeActiveModel);
    }

    @EventListener(ExitEvent.class)
    public void exitEventListener() {
        if (jmadGuiPreferences.isExitOnClose()) {
            invokeLater(this::exitJMad);
        } else {
            invokeLater(() -> setVisible(false));
        }
    }

    @EventListener(ChooseRangeEvent.class)
    public void chooseRangeEventListener() {
        invokeLater(this::showRangeDefinitionChooseDialog);
    }

    @EventListener(ChooseOpticsEvent.class)
    public void chooseOpticsEventListener() {
        invokeLater(this::showOpticsDefinitionChooseDialog);
    }

    @EventListener(ShowAboutBoxEvent.class)
    public void showAboutInfoEventListener() {
        invokeLater(this::showAboutBox);
    }

    private void setFrameCloseOperation(boolean close) {
        setDefaultCloseOperation(close ? WindowConstants.EXIT_ON_CLOSE : WindowConstants.HIDE_ON_CLOSE);
    }

    private void exitJMad() {
        modelManager.cleanup();
        System.exit(0);
    }

    private void closeActiveModel() {
        JMadModel model = modelManager.getActiveModel();
        if (model == null) {
            return;
        }
        try {
            model.cleanup();
        } catch (JMadModelException e) {
            LOGGER.error("Error while cleaning up model.", e);
        }
        modelManager.removeModel(model);
    }

    private void showAboutBox() {
        AboutDialog aboutDialog = new AboutDialog(this);
        aboutDialog.setIcon(Icon.SPLASH.getImageIcon());
        aboutDialog.setText("JMad GUI", "cern-accsoft-steering-jmad-gui",
                "(C) Copyright CERN 2008-2020  Kajetan Fuchsberger<br>and the BE-OP-LHC software team.");
        aboutDialog.show();
    }

    public void setRangeSelectionPanel(RangeSelectionPanel rangeSelectionPanel) {
        this.rangeSelectionPanel = rangeSelectionPanel;
    }

    public void setModelOpticsSelectionPanel(ModelOpticsSelectionPanel modelOpticsSelectionPanel) {
        this.modelOpticsSelectionPanel = modelOpticsSelectionPanel;
    }

    public void setUserInteractor(UserInteractor userInteractor) {
        this.userInteractor = userInteractor;
    }

    public void setJMadService(JMadService jMadService) {
        this.jMadService = jMadService;
    }

    public void setJMadModelSelectionDialogFactory(JMadModelSelectionDialogFactory jMadModelSelectionDialogFactory) {
        this.jMadModelSelectionDialogFactory = jMadModelSelectionDialogFactory;
    }

    public void setjMadModelPackageService(JMadModelPackageService jMadModelPackageService) {
        this.jMadModelPackageService = jMadModelPackageService;
    }

    public void setModelManager(JMadModelManager modelManager) {
        this.modelManager = modelManager;

        this.modelManager.addListener(new JMadModelManagerAdapter() {
            @Override
            public void changedActiveModel(JMadModel newModel) {
                updateTitleAccordingTo(newModel);
            }
        });

        if (modelManager.getActiveModel() != null) {
            updateTitleAccordingTo(modelManager.getActiveModel());
        }
    }

    public JMadGuiPreferences getJmadGuiPreferences() {
        return jmadGuiPreferences;
    }

    public void setMainPanel(JPanel mainPanel) {
        this.mainPanel = mainPanel;
    }

    public void setJMadMenuBar(JMenuBar menuBar) {
        this.jmadMenuBar = menuBar;
    }

    public void setJMadToolBar(JToolBar toolBar) {
        this.toolBar = toolBar;
    }

    public void setGuiLogPanel(GuiLogPanel guiLogPanel) {
        this.guiLogPanel = guiLogPanel;
    }

    public void setAsyncExecutor(AsyncExecutor asyncExecutor) {
        this.asyncExecutor = asyncExecutor;
    }

}