mss.View.java Source code

Java tutorial

Introduction

Here is the source code for mss.View.java

Source

/*
 * The MIT License
 *
 * Copyright 2013 Bernhard Sirlinger.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package mss;

import com.google.gson.Gson;
import de.matthiasmann.twl.renderer.lwjgl.LWJGLRenderer;
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.concurrent.atomic.AtomicReference;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.JTextField;
import javax.swing.SwingConstants;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import mss.integratoren.Integratoren;
import mss.integratoren.Rechenmodul;
import mss.util.DataFileSaver;
import mss.util.Notifications;
import mss.util.Planet;
import mss.util.Project;
import mss.util.ProjectFileSaver;
import mss.util.ScreenshotSaver;
import mss.util.Util;
import mss.util.Vektor2D;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.Color;

/**
 *
 * @author Bernhard Sirlinger
 */
public class View implements Observer, Runnable {

    private static Logger logger = LogManager.getLogger("View");
    private static boolean closeRequested = false;
    private final static AtomicReference<Dimension> newCanvasSize = new AtomicReference<>();
    private final Rechenmodul modul;
    private Thread rechenThread;
    private long speed = 100;
    private int currentIndex = 0;
    private double deltaT = 0.01;

    private enum ChangeType {

        INCREASE, DECREASE
    };

    private enum Directions {

        UP, DOWN, LEFT, RIGHT
    };

    private ArrayList<ArrayList<Planet>> results;
    private ArrayList<Planet> planets;
    private ArrayList<Planet> startPlanets;

    private long time;
    private String title;
    private final Canvas canvas = new Canvas();
    private final JFrame frame;
    private final JPanel panel = new JPanel();
    private final JSlider slider = new JSlider();
    private final JButton pauseButton;
    private final JButton playButton;
    private final JButton startCalculationButton;
    private final JButton resetButton;
    private final JButton takeScreenshotButton;
    private final JButton saveProjectButton;
    private final JButton saveDataButton;
    private final JButton zoomInButton;
    private final JButton zoomOutButton;

    private final JTabbedPane tabbedPane;
    private final JPanel planetsPanel = new JPanel();
    private final JPanel settingsPanel = new JPanel();

    private final JComboBox<String> planetsBox;

    private final JLabel errorLabel;
    private final JLabel vLabel;
    private final JLabel textLabel;
    private final JTextField labelField;
    private final JTextField vxField;
    private final JTextField vyField;
    private final JLabel coordsLabel;
    private final JTextField xField;
    private final JTextField yField;
    private final JLabel massLabel;
    private final JTextField massField;
    private final JLabel radixLabel;
    private final JTextField radixField;
    private final JLabel colorPreviewLabel;
    private final JLabel colorLabel;
    private final JTextField colorRed;
    private final JTextField colorGreen;
    private final JTextField colorBlue;
    private final JButton addPlanet;
    private final JButton removePlanet;
    private final JButton removeAllPlanets;
    private final JLabel integratorLabel;
    private final JComboBox<Integratoren> integratorBox;
    private final JLabel deltatLabel;
    private final JTextField deltatField;
    private final JCheckBox debugMode;

    private boolean isPaused = true;
    private int zoomLevel = 1;
    private final DoubleBuffer buffer;
    private boolean wasInitialized = false;
    private boolean shouldReInit = false;
    private boolean shouldTakeScreenshot = false;
    private boolean isAddingNewPlanet = false;
    private boolean debug = false;

    private String locale;
    private String lastOpenedFilePath = "";
    private String lastSavedFilePath = "";
    private String lastSavedDataFilePath = "";
    private final String standardBoxEntry;
    private final String newPlanetBoxEntry;
    private final JMenuBar menuBar;

    private HashMap<String, String> localeData;

    /**
     *
     * @param title
     */
    @SuppressWarnings("unchecked")
    public View(String title) {
        logger.entry("Constructor");
        logger.info("Starting UI");

        JPopupMenu.setDefaultLightWeightPopupEnabled(false);
        ToolTipManager.sharedInstance().setLightWeightPopupEnabled(true);
        String path = View.class.getProtectionDomain().getCodeSource().getLocation().getPath();
        try {
            path = URLDecoder.decode(path, "UTF-8");
        } catch (UnsupportedEncodingException ex) {
        }

        final File f = new File(path);
        System.setProperty("org.lwjgl.librarypath", f.getParent() + File.separator + "native");
        try {
            UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException
                | UnsupportedLookAndFeelException ex) {
        }

        selectDefaultLanguage();

        Gson gson = new Gson();
        File file = new File(f.getParent() + File.separator + "nls" + File.separator + this.locale + File.separator
                + this.locale + ".json");
        String data;
        try {
            data = new String(Files.readAllBytes(file.toPath()), "UTF-8");
            this.localeData = (HashMap<String, String>) gson.fromJson(data, HashMap.class);
        } catch (IOException ex) {
            logger.error("Could not read the locale file: {}", file.getAbsolutePath());
        }

        this.planets = new ArrayList<>();
        this.startPlanets = new ArrayList<>();

        this.modul = new Rechenmodul(Integratoren.RUNGE_KUTTA_KLASSISCH, 0.01);

        this.buffer = BufferUtils.createDoubleBuffer(16);
        this.initBuffer();

        ImageIcon img = new ImageIcon(f.getParent() + File.separator + "icon.png");
        this.title = title;
        this.frame = new JFrame(title);
        this.frame.setIconImage(img.getImage());

        this.startCalculationButton = new JButton(this.localeData.get("CALCULATE"));
        this.playButton = new JButton(this.localeData.get("PLAY"));
        this.pauseButton = new JButton(this.localeData.get("PAUSE"));
        this.resetButton = new JButton(this.localeData.get("RESET"));
        this.takeScreenshotButton = new JButton(this.localeData.get("TAKE_SCREENSHOT"));
        this.saveProjectButton = new JButton(this.localeData.get("SAVE_PROJECT"));
        this.saveDataButton = new JButton(this.localeData.get("SAVE_COMPUTED_DATA"));
        this.zoomInButton = new JButton(this.localeData.get("+"));
        this.zoomOutButton = new JButton(this.localeData.get("-"));

        /* Prevent Focus jumping */
        this.startCalculationButton.setFocusable(false);
        this.playButton.setFocusable(false);
        this.pauseButton.setFocusable(false);
        this.resetButton.setFocusable(false);
        this.takeScreenshotButton.setFocusable(false);
        this.saveProjectButton.setFocusable(false);
        this.saveDataButton.setFocusable(false);
        this.zoomInButton.setFocusable(false);
        this.zoomOutButton.setFocusable(false);

        this.slider.setEnabled(false);
        this.slider.setMinimumSize(new Dimension(800, this.slider.getHeight()));

        this.panel.setLayout(new GridBagLayout());
        this.panel.setPreferredSize(new Dimension(800, 100));
        this.panel.setBackground(java.awt.Color.LIGHT_GRAY);

        GridBagConstraints c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 0;
        c.gridy = 0;
        c.gridwidth = 9;
        c.ipadx = 0;
        c.ipady = 0;
        c.weightx = 1.0;
        c.anchor = GridBagConstraints.FIRST_LINE_START;
        this.panel.add(this.slider, c);
        c.gridy = 1;
        c.gridwidth = 1;
        c.weighty = 1.0;
        this.panel.add(this.startCalculationButton, c);
        c.gridx = 1;
        this.panel.add(this.playButton, c);
        c.gridx = 2;
        this.panel.add(this.pauseButton, c);
        c.gridx = 3;
        this.panel.add(this.resetButton, c);
        c.gridx = 4;
        this.panel.add(this.takeScreenshotButton, c);
        c.gridx = 5;
        this.panel.add(this.saveProjectButton, c);
        c.gridx = 6;
        this.panel.add(this.saveDataButton, c);
        c.gridx = 7;
        this.panel.add(this.zoomInButton, c);
        c.gridx = 8;
        this.panel.add(this.zoomOutButton, c);

        this.tabbedPane = new JTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
        this.tabbedPane.setPreferredSize(new Dimension(300, this.tabbedPane.getHeight()));
        this.tabbedPane.addTab(this.localeData.get("START_VALUES"), this.planetsPanel);
        this.tabbedPane.addTab(this.localeData.get("SETTINGS"), this.settingsPanel);

        this.planetsPanel.setLayout(new GridBagLayout());

        this.standardBoxEntry = this.localeData.get("CHOOSE_A_PLANET");
        this.newPlanetBoxEntry = this.localeData.get("ADD_NEW_PLANET");

        this.planetsBox = new JComboBox<>();
        this.planetsBox.addItem(this.standardBoxEntry);
        this.planetsBox.addItem(this.newPlanetBoxEntry);

        this.errorLabel = new JLabel();
        this.textLabel = new JLabel(this.localeData.get("LABEL"));
        this.textLabel.setHorizontalAlignment(SwingConstants.CENTER);
        this.textLabel.setVerticalAlignment(SwingConstants.CENTER);
        this.labelField = new JTextField();
        this.vLabel = new JLabel(this.localeData.get("V_IN_MS"));
        this.vLabel.setHorizontalAlignment(SwingConstants.CENTER);
        this.vLabel.setVerticalAlignment(SwingConstants.CENTER);
        this.vxField = new JTextField();
        this.vyField = new JTextField();
        this.coordsLabel = new JLabel(this.localeData.get("COORDINATES"));
        this.coordsLabel.setHorizontalAlignment(SwingConstants.CENTER);
        this.coordsLabel.setVerticalAlignment(SwingConstants.CENTER);
        this.xField = new JTextField();
        this.yField = new JTextField();
        this.massLabel = new JLabel(this.localeData.get("MASS"));
        this.massLabel.setHorizontalAlignment(SwingConstants.CENTER);
        this.massLabel.setVerticalAlignment(SwingConstants.CENTER);
        this.massField = new JTextField();
        this.radixLabel = new JLabel(this.localeData.get("RADIX"));
        this.radixLabel.setHorizontalAlignment(SwingConstants.CENTER);
        this.radixLabel.setVerticalAlignment(SwingConstants.CENTER);
        this.radixField = new JTextField();
        this.colorPreviewLabel = new JLabel();
        this.colorLabel = new JLabel(this.localeData.get("COLOR"));
        this.colorRed = new JTextField();
        this.colorGreen = new JTextField();
        this.colorBlue = new JTextField();

        this.addPlanet = new JButton(this.localeData.get("ADD_PLANET"));
        this.addPlanet.setToolTipText(this.localeData.get("ADD_PLANET"));
        this.addPlanet.setFocusable(false);
        this.removePlanet = new JButton(this.localeData.get("REMOVE_PLANET"));
        this.removePlanet.setToolTipText(this.localeData.get("REMOVE_PLANET"));
        this.removePlanet.setFocusable(false);
        this.removeAllPlanets = new JButton(this.localeData.get("REMOVE_ALL_PLANETS"));
        this.removeAllPlanets.setToolTipText(this.localeData.get("REMOVE_ALL_PLANETS"));
        this.removeAllPlanets.setFocusable(false);

        /*Settings*/
        this.integratorLabel = new JLabel(this.localeData.get("NUMERICAL_METHOD"));
        this.integratorBox = new JComboBox<>();
        this.integratorBox.addItem(Integratoren.EULER);
        this.integratorBox.addItem(Integratoren.RUNGE_KUTTA_KLASSISCH);
        this.integratorBox.setSelectedIndex(1);

        this.deltatLabel = new JLabel(this.localeData.get("DELTA_T"));
        this.deltatField = new JTextField("" + this.deltaT);

        this.debugMode = new JCheckBox(this.localeData.get("DEBUG_MODE"), false);

        this.settingsPanel.add(this.integratorLabel);
        this.settingsPanel.add(this.integratorBox);
        this.settingsPanel.add(this.deltatLabel);
        this.settingsPanel.add(this.deltatField);
        this.settingsPanel.add(this.debugMode);
        /*Settings*/

        c = new GridBagConstraints();
        c.fill = GridBagConstraints.HORIZONTAL;
        c.gridx = 0;
        c.gridy = 0;
        c.gridwidth = 3;
        c.weightx = 1.0;
        c.weighty = 0.1;
        c.anchor = GridBagConstraints.FIRST_LINE_START;
        this.planetsPanel.add(this.planetsBox, c);
        c.gridy = 1;
        c.gridwidth = 1;
        c.weighty = 0.0;
        this.planetsPanel.add(this.textLabel, c);
        c.gridx = 1;
        c.gridwidth = 2;
        this.planetsPanel.add(this.labelField, c);
        c.gridy = 2;
        c.gridx = 0;
        c.gridwidth = 1;
        this.planetsPanel.add(this.vLabel, c);
        c.gridx = 1;
        this.planetsPanel.add(this.vxField, c);
        c.gridx = 2;
        this.planetsPanel.add(this.vyField, c);
        c.gridy = 3;
        c.gridx = 0;
        this.planetsPanel.add(this.coordsLabel, c);
        c.gridx = 1;
        this.planetsPanel.add(this.xField, c);
        c.gridx = 2;
        this.planetsPanel.add(this.yField, c);
        c.gridy = 4;
        c.gridx = 0;
        this.planetsPanel.add(this.massLabel, c);
        c.gridx = 1;
        this.planetsPanel.add(this.massField, c);
        c.gridy = 5;
        c.gridx = 0;
        this.planetsPanel.add(this.radixLabel, c);
        c.gridx = 1;
        this.planetsPanel.add(this.radixField, c);
        c.gridy = 6;
        c.gridx = 0;
        this.planetsPanel.add(this.addPlanet, c);
        c.gridx = 1;
        this.planetsPanel.add(this.removePlanet, c);
        c.gridx = 2;
        this.planetsPanel.add(this.removeAllPlanets, c);

        this.addListeners();
        this.addPlanetsUIListeners();

        this.menuBar = new JMenuBar();
        this.frame.setJMenuBar(this.menuBar);

        JMenu fileMenu = new JMenu(this.localeData.get("FILE"));
        JMenu helpMenu = new JMenu(this.localeData.get("HELP"));

        JMenuItem openFile = new JMenuItem(this.localeData.get("OPEN_PROJECT"));
        openFile.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                isPaused = true;
                openFile();
                canvas.requestFocus();
            }
        });
        fileMenu.add(openFile);

        JMenuItem pause = new JMenuItem(this.localeData.get("PAUSE"));
        pause.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (!isPaused) {
                    isPaused = true;
                    ((JMenuItem) e.getSource()).setText(localeData.get("RESTART"));
                } else {
                    isPaused = false;
                    ((JMenuItem) e.getSource()).setText(localeData.get("PAUSE"));
                }
                canvas.requestFocus();
            }
        });
        fileMenu.add(pause);

        JMenuItem saveDataToFile = new JMenuItem(this.localeData.get("SAVE_COMPUTED_DATA"));
        saveDataToFile.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                saveData();
                canvas.requestFocus();
            }
        });
        fileMenu.add(saveDataToFile);

        JMenuItem about = new JMenuItem(this.localeData.get("ABOUT"));
        about.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                ((JMenuItem) e.getSource()).setName("");
                if (!isPaused) {
                    isPaused = true;
                    ((JMenuItem) e.getSource()).setName("selfPaused");
                }
                showAboutDialog();
                canvas.requestFocus();
            }
        });
        helpMenu.add(about);

        this.menuBar.add(fileMenu);
        this.menuBar.add(helpMenu);

        logger.exit();
    }

    private void initBuffer() {
        this.buffer.put(0, this.zoomLevel);
        this.buffer.put(1, 0);
        this.buffer.put(2, 0);
        this.buffer.put(3, 0);
        this.buffer.put(4, 0);
        this.buffer.put(5, this.zoomLevel);
        this.buffer.put(6, 0);
        this.buffer.put(7, 0);
        this.buffer.put(8, 0);
        this.buffer.put(9, 0);
        this.buffer.put(10, this.zoomLevel);
        this.buffer.put(11, 0);
        this.buffer.put(12, 0);
        this.buffer.put(13, 0);
        this.buffer.put(14, 0);
        this.buffer.put(15, this.zoomLevel);
    }

    private void addListeners() {
        this.slider.addChangeListener(new ChangeListener() {
            @Override
            public void stateChanged(ChangeEvent e) {
                JSlider slider = (JSlider) e.getSource();
                currentIndex = slider.getValue();
                planets = results.get(currentIndex);
                if (slider.isFocusOwner()) {
                    isPaused = true;

                }
            }
        });

        this.canvas.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                newCanvasSize.set(new Dimension(canvas.getSize().width, canvas.getSize().height));
            }
        });

        this.frame.addComponentListener(new ComponentAdapter() {
            @Override
            public void componentResized(ComponentEvent e) {
                if (wasInitialized) {
                    shouldReInit = true;
                }
            }
        });

        this.frame.addWindowListener(new WindowAdapter() {
            private boolean wasAlreadyPaused;

            @Override
            public void windowActivated(WindowEvent e) {
                if (!this.wasAlreadyPaused) {
                    isPaused = false;
                }
                canvas.requestFocusInWindow();
            }

            @Override
            public void windowDeactivated(WindowEvent e) {
                if (!isPaused) {
                    isPaused = true;
                    this.wasAlreadyPaused = false;
                } else {
                    this.wasAlreadyPaused = true;
                }
                super.windowLostFocus(e);
            }

            @Override
            public void windowClosing(WindowEvent e) {
                closeRequested = true;
            }
        });

        this.playButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                canvas.requestFocusInWindow();
                isPaused = false;
            }
        });

        this.pauseButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                isPaused = true;
            }
        });

        this.startCalculationButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                canvas.requestFocus();
                modul.setData(startPlanets);
                rechenThread = new Thread(modul);
                rechenThread.setDaemon(true);
                rechenThread.start();
                isPaused = true;
                startCalculationButton.setEnabled(false);
            }
        });

        this.resetButton.addActionListener(new ActionListener() {
            @Override
            @SuppressWarnings("unchecked")
            public void actionPerformed(ActionEvent e) {
                if (results != null) {
                    planets = (ArrayList<Planet>) startPlanets.clone();
                    isPaused = true;
                    currentIndex = 0;
                    slider.setValue(0);
                }
            }
        });

        this.takeScreenshotButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                shouldTakeScreenshot = true;
            }
        });

        this.saveProjectButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                saveProject();
            }
        });

        this.saveDataButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                saveData();
            }
        });

        this.zoomInButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                changeZoomFactor(ChangeType.INCREASE, false);
                shouldReInit = true;
            }
        });

        this.zoomOutButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                changeZoomFactor(ChangeType.DECREASE, false);
                shouldReInit = true;
            }
        });

        this.planetsBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                int index = ((JComboBox) e.getSource()).getSelectedIndex();
                try {
                    isAddingNewPlanet = false;
                    if (index > 1) {
                        Planet temp = startPlanets.get(index - 2);
                        labelField.setText(temp.getLabel());
                        labelField.setEnabled(true);
                        vxField.setText("" + temp.getV().getX());
                        vxField.setEnabled(true);
                        vyField.setText("" + temp.getV().getY());
                        vyField.setEnabled(true);
                        xField.setText("" + temp.getCoords().getX());
                        xField.setEnabled(true);
                        yField.setText("" + temp.getCoords().getY());
                        yField.setEnabled(true);
                        massField.setText("" + temp.getMass());
                        massField.setEnabled(true);
                        radixField.setText("" + temp.getRadix());
                        radixField.setEnabled(true);
                        addPlanet.setEnabled(false);
                        removePlanet.setEnabled(true);
                    } else if (index == 1) {
                        isAddingNewPlanet = true;
                        labelField.setEnabled(true);
                        labelField.setText("");
                        vxField.setEnabled(true);
                        vxField.setText("");
                        vyField.setEnabled(true);
                        vyField.setText("");
                        xField.setEnabled(true);
                        xField.setText("");
                        yField.setEnabled(true);
                        yField.setText("");
                        massField.setEnabled(true);
                        massField.setText("");
                        radixField.setEnabled(true);
                        radixField.setText("");
                        addPlanet.setEnabled(true);
                        removePlanet.setEnabled(false);
                    } else {
                        labelField.setText("");
                        labelField.setEnabled(false);
                        vxField.setText("");
                        vxField.setEnabled(false);
                        vyField.setText("");
                        vyField.setEnabled(false);
                        xField.setText("");
                        xField.setEnabled(false);
                        yField.setText("");
                        yField.setEnabled(false);
                        massField.setText("");
                        massField.setEnabled(false);
                        radixField.setText("");
                        radixField.setEnabled(false);
                        addPlanet.setEnabled(false);
                        removePlanet.setEnabled(false);
                    }
                } catch (java.lang.IllegalStateException ex) {
                }
            }
        });

        this.integratorBox.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                modul.setIntegrator((Integratoren) ((JComboBox) e.getSource()).getSelectedItem());
            }
        });

        this.deltatField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                String currentValue = ((JTextField) e.getSource()).getText();
                if (currentValue.contains(",")) {
                    currentValue = currentValue.replace(',', '.');
                }

                try {
                    deltaT = Double.parseDouble(currentValue);
                    modul.setDeltaT(deltaT);
                } catch (NumberFormatException ex) {
                    showErrorDialog("Invalid Value", "The value \"" + currentValue + "\" for deltaT is not valid.");
                    deltatField.setText("" + deltaT);
                }
            }
        });

        this.debugMode.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                debug = !debug;
            }
        });

        this.addPlanet.addActionListener(new ActionListener() {
            @Override
            @SuppressWarnings("unchecked")
            public void actionPerformed(ActionEvent e) {
                String error = "", label, temp;

                Vektor2D coords = new Vektor2D(), v = new Vektor2D();
                double mass = 0, radix = 0;

                label = labelField.getText().trim();

                temp = xField.getText().trim();
                if (!temp.isEmpty()) {
                    try {
                        coords.setX(Double.parseDouble(temp));
                    } catch (NumberFormatException ex) {
                        error += "The value \"" + temp + " \" for the x coordinate is not valid.\n";
                    }
                }

                temp = yField.getText().trim();
                if (!temp.isEmpty()) {
                    try {
                        coords.setY(Double.parseDouble(temp));
                    } catch (NumberFormatException ex) {
                        error += "The value \"" + temp + " \" for the y coordinate is not valid.\n";
                    }
                }

                temp = massField.getText().trim();
                if (!temp.isEmpty()) {
                    try {
                        mass = Double.parseDouble(temp);
                    } catch (NumberFormatException ex) {
                        error += "The value \"" + temp + " \" for the mass is not valid.\n";
                    }
                }

                temp = radixField.getText().trim();
                if (!temp.isEmpty()) {
                    try {
                        radix = Double.parseDouble(temp);
                    } catch (NumberFormatException ex) {
                        error += "The value \"" + temp + " \" for the radix is not valid.\n";
                    }
                }

                temp = vxField.getText().trim();
                if (!temp.isEmpty()) {
                    try {
                        v.setX(Double.parseDouble(temp));
                    } catch (NumberFormatException ex) {
                        error += "The value \"" + temp + " \" for the x component of v is not valid.\n";
                    }
                }

                temp = vyField.getText().trim();
                if (!temp.isEmpty()) {
                    try {
                        v.setY(Double.parseDouble(temp));
                    } catch (NumberFormatException ex) {
                        error += "The value \"" + temp + " \" for the y component of v is not valid.\n";
                    }
                }

                if (error.isEmpty()) {
                    Color c = new Color((int) (Math.random() * 255), (int) (Math.random() * 255),
                            (int) (Math.random() * 255));
                    startPlanets.add(new Planet(label, coords, mass, radix, v, c));
                    planets = (ArrayList<Planet>) startPlanets.clone();
                    planetsBox.addItem(label);
                    planetsBox.setSelectedIndex(planetsBox.getItemCount() - 1);
                } else {
                    showErrorDialog("Invalid Component values", error);
                }
            }
        });

        this.removePlanet.addActionListener(new ActionListener() {
            @Override
            @SuppressWarnings("unchecked")
            public void actionPerformed(ActionEvent e) {
                int index = planetsBox.getSelectedIndex() - 2;

                planetsBox.setSelectedIndex(0);

                startPlanets.remove(index);
                planets = (ArrayList<Planet>) startPlanets.clone();
                updateComboBoxes();
            }
        });

        this.removeAllPlanets.addActionListener(new ActionListener() {
            @Override
            @SuppressWarnings("unchecked")
            public void actionPerformed(ActionEvent e) {
                startPlanets = new ArrayList<>();
                planets = (ArrayList<Planet>) startPlanets.clone();

                planetsBox.removeAllItems();
                planetsBox.addItem(standardBoxEntry);
                planetsBox.addItem(newPlanetBoxEntry);
                planetsBox.setSelectedIndex(0);
            }
        });
    }

    private void addPlanetsUIListeners() {
        this.labelField.addCaretListener(new CaretListener() {
            @Override
            public void caretUpdate(CaretEvent e) {
                if (isAddingNewPlanet) {
                    return;
                }
                String currentValue = labelField.getText();
                int i = planetsBox.getSelectedIndex();
                if (i < 2) {
                    return;
                }
                Planet temp = startPlanets.get(i - 2);

                temp.setLabel(currentValue);
                planetsBox.insertItemAt(currentValue, i);
                planetsBox.removeItemAt(i + 1);
                planetsBox.setSelectedIndex(i);
                planetsBox.repaint();
                startPlanets.set(i - 2, temp);
            }
        });

        this.vxField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                if (isAddingNewPlanet) {
                    return;
                }
                String currentValue = vxField.getText();
                int i = planetsBox.getSelectedIndex();
                Planet temp = startPlanets.get(i - 2);

                try {
                    double vx = Double.parseDouble(currentValue);
                    temp.setV(new Vektor2D(vx, temp.getV().getY()));
                    startPlanets.set(i - 2, temp);
                    vxField.setText("" + vx);
                } catch (NumberFormatException ex) {
                    showErrorDialog("Invalid Value",
                            "The value \"" + currentValue + "\" for the x component of v is not valid.");
                    vxField.setText("" + temp.getV().getX());
                }
            }
        });

        this.vyField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                if (isAddingNewPlanet) {
                    return;
                }
                String currentValue = vyField.getText();
                int i = planetsBox.getSelectedIndex();
                Planet temp = startPlanets.get(i - 2);

                try {
                    double vy = Double.parseDouble(currentValue);
                    temp.setV(new Vektor2D(temp.getV().getX(), vy));
                    startPlanets.set(i - 2, temp);
                    vyField.setText("" + vy);
                } catch (NumberFormatException ex) {
                    showErrorDialog("Invalid Value",
                            "The value \"" + currentValue + "\" for the y component of v is not valid.");
                    vyField.setText("" + temp.getV().getY());
                }
            }
        });

        this.xField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                if (isAddingNewPlanet) {
                    return;
                }
                String currentValue = xField.getText();
                int i = planetsBox.getSelectedIndex();
                Planet temp = startPlanets.get(i - 2);

                try {
                    double x = Double.parseDouble(currentValue);
                    temp.setCoords(new Vektor2D(x, temp.getCoords().getY()));
                    startPlanets.set(i - 2, temp);
                    xField.setText("" + x);
                } catch (NumberFormatException ex) {
                    showErrorDialog("Invalid Value", "The value \"" + currentValue
                            + "\" for the x component of the coordinates is not valid.");
                    xField.setText("" + temp.getCoords().getX());
                }
            }
        });

        this.yField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                if (isAddingNewPlanet) {
                    return;
                }
                String currentValue = yField.getText();
                int i = planetsBox.getSelectedIndex();
                Planet temp = startPlanets.get(i - 2);

                try {
                    double y = Double.parseDouble(currentValue);
                    temp.setCoords(new Vektor2D(temp.getCoords().getX(), y));
                    startPlanets.set(i - 2, temp);
                    ((JTextField) (e.getSource())).setText("" + y);
                } catch (NumberFormatException ex) {
                    showErrorDialog("Invalid Value", "The value \"" + currentValue
                            + "\" for the y component of the coordinates is not valid.");
                    yField.setText("" + temp.getCoords().getY());
                }
            }
        });

        this.massField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                if (isAddingNewPlanet) {
                    return;
                }
                String currentValue = massField.getText();
                int i = planetsBox.getSelectedIndex();
                Planet temp = startPlanets.get(i - 2);

                try {
                    double mass = Double.parseDouble(currentValue);
                    temp.setMass(mass);
                    startPlanets.set(i - 2, temp);
                    massField.setText("" + mass);
                } catch (NumberFormatException ex) {
                    showErrorDialog("Invalid Value",
                            "The value \"" + currentValue + "\" for the mass is not valid.");
                    massField.setText("" + temp.getMass());
                }
            }
        });

        this.radixField.addFocusListener(new FocusAdapter() {
            @Override
            public void focusLost(FocusEvent e) {
                if (isAddingNewPlanet) {
                    return;
                }
                String currentValue = radixField.getText();
                int i = planetsBox.getSelectedIndex();
                Planet temp = startPlanets.get(i - 2);

                try {
                    double radix = Double.parseDouble(currentValue);
                    temp.setRadix(radix);
                    startPlanets.set(i - 2, temp);
                    radixField.setText("" + radix);
                } catch (NumberFormatException ex) {
                    showErrorDialog("Invalid Value",
                            "The value \"" + currentValue + "\" for the radix is not valid.");
                    radixField.setText("" + temp.getRadix());
                }
            }
        });
    }

    public void init() {
        this.modul.registerObserver("view", this);
        updateComboBoxes();

        try {
            Display.setParent(this.canvas);
            Keyboard.enableRepeatEvents(true);
            this.frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
            this.frame.setLayout(new BorderLayout());
            this.frame.add(this.tabbedPane, BorderLayout.LINE_START);
            this.frame.add(this.canvas, BorderLayout.CENTER);
            this.frame.add(this.panel, BorderLayout.PAGE_END);
            this.frame.setPreferredSize(new Dimension(1024, 786));
            this.frame.setMinimumSize(new Dimension(800, 600));
            this.frame.pack();

            this.frame.setVisible(true);

            Display.setTitle(this.title);
            Display.setResizable(true);
            Display.setFullscreen(false);
            Display.create();
            this.initOpenGL();
            this.wasInitialized = true;
            LWJGLRenderer renderer = new LWJGLRenderer();

            Dimension newDim;

            while (!Display.isCloseRequested() && !View.closeRequested) {
                if (this.shouldReInit) {
                    this.initOpenGL();
                    this.shouldReInit = false;
                }

                if (this.shouldTakeScreenshot) {
                    this.saveScreenshot();
                    this.shouldTakeScreenshot = false;
                }

                GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
                newDim = newCanvasSize.getAndSet(null);
                checkKeyInput();
                if (newDim != null) {
                    GL11.glViewport(0, 0, newDim.width, newDim.height);
                    renderer.syncViewportSize();
                }

                for (int i = 0; i < this.planets.size(); i++) {
                    this.planets.get(i).draw2D(this.debug);
                }

                if (!this.isPaused && this.results != null && this.currentIndex < this.results.size() - 1
                        && this.getDelta() / this.deltaT >= this.speed) {
                    this.planets = this.results.get(this.currentIndex);
                    int add = (int) (1 / deltaT);
                    if (add == 0) {
                        add = 1;
                    }
                    this.time = this.getTime();
                    this.currentIndex += add;
                    this.slider.setValue(this.slider.getValue() + add);
                }

                checkKeyInput();
                checkMouseInput();
                Display.update();
                Display.sync(60);
            }

            Display.destroy();
            this.frame.dispose();
        } catch (LWJGLException e) {
            System.out.println(e.getMessage());
            System.exit(-1);
        }
    }

    private void checkKeyInput() {
        while (Keyboard.next()) {
            if (!Keyboard.getEventKeyState()) {
                switch (Keyboard.getEventKey()) {
                case Keyboard.KEY_F1:
                    this.saveScreenshot();
                    break;
                case Keyboard.KEY_SPACE:
                    this.isPaused = !this.isPaused;
                    break;
                }
            }

            switch (Keyboard.getEventKey()) {
            case Keyboard.KEY_UP:
                this.changeTranslationMatrix(Directions.UP);
                break;
            case Keyboard.KEY_DOWN:
                this.changeTranslationMatrix(Directions.DOWN);
                break;
            case Keyboard.KEY_LEFT:
                this.changeTranslationMatrix(Directions.LEFT);
                break;
            case Keyboard.KEY_RIGHT:
                this.changeTranslationMatrix(Directions.RIGHT);
                break;
            case Keyboard.KEY_0:
                Keyboard.poll();
                if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) {
                    this.resetScaleMatrix();
                }
                break;
            case Keyboard.KEY_C:
                this.resetTranslationMatrix();
                break;
            case Keyboard.KEY_ADD:
                if (this.speed >= 5 / this.deltaT) {
                    this.speed -= 5 / this.deltaT;
                }
                break;
            case Keyboard.KEY_SUBTRACT:
                this.speed += 5 / this.deltaT;
                break;
            }
        }
    }

    private void initOpenGL() {
        GL11.glMatrixMode(GL11.GL_PROJECTION);

        GL11.glLoadMatrix(this.buffer);

        GL11.glOrtho(-100, 100, -100, 100, 1, -1);

        GL11.glMatrixMode(GL11.GL_MODELVIEW);

        GL11.glEnable(GL11.GL_BLEND);
        GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
    }

    private void showAboutDialog() {
        JDialog dialog = new JDialog(this.frame, true);
        dialog.setSize(200, 200);
        dialog.setLocation(this.frame.getX() + this.frame.getWidth() / 2 - 100,
                this.frame.getY() + this.frame.getHeight() / 2 - 100);
        dialog.setEnabled(true);
        dialog.setVisible(true);
        dialog.addWindowListener(new WindowAdapter() {
            @Override
            public void windowDeactivated(WindowEvent e) {
                isPaused = false;
                e.getWindow().dispose();
            }
        });
    }

    private void saveScreenshot() {
        final File f = new File(View.class.getProtectionDomain().getCodeSource().getLocation().getPath());
        GL11.glReadBuffer(GL11.GL_FRONT);
        int width = Display.getWidth();
        int height = Display.getHeight();
        ByteBuffer byteBuffer = BufferUtils.createByteBuffer(width * height * 4);
        GL11.glReadPixels(0, 0, width, height, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, byteBuffer);
        ScreenshotSaver saver = new ScreenshotSaver(byteBuffer, f.getParent(), width, height);
        saver.start();
    }

    private void saveProject() {
        boolean wasSelfPaused = !this.isPaused;

        this.isPaused = true;

        JFileChooser fileChooser;

        if (this.lastOpenedFilePath.isEmpty()) {
            fileChooser = new JFileChooser();
        } else {
            fileChooser = new JFileChooser(this.lastOpenedFilePath);
        }

        int state = fileChooser.showSaveDialog(this.frame);

        if (state == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fileChooser.getSelectedFile();
            this.lastSavedFilePath = selectedFile.getAbsolutePath();

            ProjectFileSaver saver = new ProjectFileSaver(this.lastSavedFilePath, this.startPlanets,
                    this.modul.getIntegrator(), this.deltaT);
            saver.start();
        }

        if (wasSelfPaused) {
            this.isPaused = false;
        }
    }

    private void saveData() {
        if (this.results == null) {
            return;
        }

        boolean wasSelfPaused = !this.isPaused;

        this.isPaused = true;

        JFileChooser fileChooser;

        if (this.lastSavedDataFilePath.isEmpty()) {
            fileChooser = new JFileChooser();
        } else {
            fileChooser = new JFileChooser(this.lastSavedDataFilePath);
        }

        int state = fileChooser.showSaveDialog(this.frame);

        if (state == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fileChooser.getSelectedFile();
            this.lastSavedDataFilePath = selectedFile.getAbsolutePath();

            DataFileSaver saver = new DataFileSaver(this.lastSavedDataFilePath, this.deltaT, this.results);
            saver.start();
        }

        if (wasSelfPaused) {
            this.isPaused = false;
        }
    }

    @SuppressWarnings("unchecked")
    private void openFile() {
        JFileChooser fileChooser;

        if (this.lastOpenedFilePath.isEmpty()) {
            fileChooser = new JFileChooser(System.getProperty("user.home"));
        } else {
            fileChooser = new JFileChooser(this.lastOpenedFilePath);
        }
        int state = fileChooser.showOpenDialog(this.frame);

        if (state == JFileChooser.APPROVE_OPTION) {
            File selectedFile = fileChooser.getSelectedFile();
            this.lastOpenedFilePath = selectedFile.getAbsolutePath();
            HashMap<String, Object> dataFromDataFile = Util.getDataFromDataFile(selectedFile);
            if (dataFromDataFile.get("Error") != null && !"".equals((String) dataFromDataFile.get("Error"))) {
                this.showErrorDialog(this.localeData.get("ERROR_OPENING_PROJECT_FILE"),
                        (String) dataFromDataFile.get("Error"));
            } else {
                Project project = (Project) dataFromDataFile.get("Project");
                if (project.planets.isEmpty()) {
                    this.showErrorDialog(this.localeData.get("ERROR_OPENING_PROJECT_FILE"),
                            "No Planets are defined in this File");
                } else {
                    this.planets = project.planets;
                    this.startPlanets = (ArrayList<Planet>) this.planets.clone();
                    this.deltaT = project.deltaT;
                    this.modul.setDeltaT(this.deltaT);
                    this.deltatField.setText("" + this.deltaT);
                    this.modul.setIntegrator(project.integrator);
                    this.integratorBox.setSelectedItem(project.integrator);
                    this.speed = (long) (1 / this.deltaT);
                    updateComboBoxes();
                }
            }
            this.isPaused = true;
        } else {
            this.isPaused = false;
        }
    }

    private void updateComboBoxes() {
        int size = this.startPlanets.size();
        String temp;

        this.planetsBox.removeAllItems();
        this.planetsBox.addItem(this.standardBoxEntry);
        this.planetsBox.addItem(this.newPlanetBoxEntry);

        for (int i = 0; i < size; i++) {
            temp = this.startPlanets.get(i).getLabel();
            this.planetsBox.addItem(temp);
        }

        this.planetsBox.setSelectedIndex(0);
    }

    private void showErrorDialog(String title, String errors) {
        JOptionPane.showMessageDialog(this.frame, errors, title, JOptionPane.ERROR_MESSAGE);
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
        Display.setTitle(this.title);
    }

    public void setPlanets(ArrayList<Planet> planets) {
        this.planets = planets;
    }

    @Override
    public void run() {
        this.init();
    }

    @Override
    public void notify(Notifications type, String data) {
    }

    @Override
    public void sendPlanets(Notifications type, ArrayList<ArrayList<Planet>> planets) {
        this.results = planets;
        this.initSlider();
        this.isPaused = false;
        this.startCalculationButton.setEnabled(true);
    }

    private void initSlider() {
        this.slider.setMaximum(this.results.size() - 2);
        this.slider.setMinorTickSpacing(1);
        this.slider.setValue(0);
        this.slider.setEnabled(true);
    }

    private void checkMouseInput() {
        int delta = Mouse.getDWheel();

        if (delta > 0) {
            this.changeZoomFactor(ChangeType.INCREASE, true);
        } else if (delta < 0) {
            this.changeZoomFactor(ChangeType.DECREASE, true);
        }
    }

    private void resetScaleMatrix() {
        this.zoomLevel = 0;
        this.buffer.put(0, this.zoomLevel);
        this.buffer.put(5, this.zoomLevel);
        this.buffer.put(10, this.zoomLevel);
        this.initOpenGL();
    }

    private void resetTranslationMatrix() {
        this.buffer.put(12, 0);
        this.buffer.put(13, 0);
        this.initOpenGL();
    }

    private void changeTranslationMatrix(Directions direction) {
        switch (direction) {
        case UP:
            this.buffer.put(13, this.buffer.get(13) + 0.01);
            break;
        case DOWN:
            this.buffer.put(13, this.buffer.get(13) - 0.01);
            break;
        case LEFT:
            this.buffer.put(12, this.buffer.get(12) - 0.01);
            break;
        case RIGHT:
            this.buffer.put(12, this.buffer.get(12) + 0.01);
            break;
        }

        this.initOpenGL();
    }

    private void changeZoomFactor(ChangeType change, boolean shouldReInit) {
        switch (change) {
        case DECREASE:
            if (this.zoomLevel != 1) {
                this.zoomLevel -= 1;
            } else {
                this.zoomLevel = -1;
            }
            break;
        case INCREASE:
            if (this.zoomLevel != -1) {
                this.zoomLevel += 1;
            } else {
                this.zoomLevel = 1;
            }
            break;
        }

        if (this.zoomLevel > 0) {
            this.buffer.put(0, this.zoomLevel);
            this.buffer.put(5, this.zoomLevel);
            this.buffer.put(10, this.zoomLevel);
        } else if (this.zoomLevel < 0) {
            this.buffer.put(0, 1.0 / -this.zoomLevel);
            this.buffer.put(5, 1.0 / -this.zoomLevel);
            this.buffer.put(10, 1.0 / -this.zoomLevel);
        }
        if (shouldReInit) {
            this.initOpenGL();
        }
    }

    private long getTime() {
        return (Sys.getTime() * 1000 / Sys.getTimerResolution());
    }

    private long getDelta() {
        long current_time = this.getTime();
        long delta = current_time - this.time;

        return delta;
    }

    private void selectDefaultLanguage() {
        switch (System.getProperty("user.language")) {
        case "de":
            this.locale = "de";
            break;

        default:
            this.locale = "en";
            break;
        }
    }
}