package view; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import javax.swing.*; import controller.Humidity; import controller.World; import main.*; /** * This class is the main class of the view package. It combines all the different * GUI components required for the programme. * * @author Daniel Vedder * @version 29.8.2014 */ public class GUI extends JFrame { private static final long serialVersionUID = 4727895060816956404L; private Box information; private JMenuBar menubar; private JMenu file, configuration, help_menu; private JMenuItem new_run, exit, programConfigBox, simConfigBox, genomeConfigBox, configFileDialog, help, about; private JLabel update_counter, herbivore_counter, carnivore_counter, generation_counter, grass_counter; private JComboBox<String> humidityChooser; private JTextArea ticker; //XXX Remove this at some point? private JTextField stopAtField; private JCheckBox disableDisplay; private JScrollPane scrollticker, scrollscreen; private JButton run, next; private JSlider speedSlider; private Display display; private ProgramConfig programConfig; private SimulationConfig simulationConfig; private GenomeConfig genomeConfig; private JFileChooser configChooser; private HelpWindow helpWindow; /** * The constructor. */ public GUI() { EcologiaIO.debug("Creating GUI"); this.setTitle("Ecologia"); this.setSize(1000, 560); this.setDefaultCloseOperation(EXIT_ON_CLOSE); createMenu(); addInformationPanel(); addDisplay(); programConfig = new ProgramConfig(); simulationConfig = new SimulationConfig(); genomeConfig = new GenomeConfig(); configChooser = new JFileChooser(System.getProperty("user.dir")); helpWindow = new HelpWindow(); this.setVisible(true); } /** * Update the GUI. */ public synchronized void update() { EcologiaIO.debug("GUI: updating display."); //Update the display if (!disableDisplay.isSelected()) { display.update(); } displayNews(); //Make sure the "run" button is displaying the right text if (World.getInstance().isRunning()) run.setText("Stop"); else run.setText("Start"); //Update the humidity from the combo box Humidity setHumidity = Humidity.fromString((String) humidityChooser.getSelectedItem()); if (setHumidity != World.getInstance().getHumidity()) { World.getInstance().setHumidity(setHumidity); EcologiaIO.log("Humidity set to "+setHumidity.getString()); } //Update the simulation speed from the speed slider int setSpeed = speedSlider.getMaximum() - speedSlider.getValue(); World.getInstance().setTimelapse(setSpeed); //Update the stopAt variable from user input try { World.getInstance().setStopAt(Integer.parseInt((stopAtField.getText()))); } catch (NumberFormatException nfe) {} //Update the various counters update_counter.setText("Updates: "+ World.getInstance().getTurn()); herbivore_counter.setText("Herbivores: "+ World.getInstance().getHerbivoreCount()); carnivore_counter.setText("Carnivores: "+ World.getInstance().getCarnivoreCount()); generation_counter.setText("Generations: "+World.getInstance().getGeneration()); grass_counter.setText("Grass density: "+World.getInstance().getAverageGrassDensity()); humidityChooser.setSelectedItem(World.getInstance().getHumidity().getString()); } /** * Add the menubar */ private void createMenu() { EcologiaIO.debug("GUI: creating menubar."); menubar = new JMenuBar(); file = new JMenu("File"); configuration = new JMenu("Configuration"); help_menu = new JMenu("Help"); new_run = new JMenuItem("New Run"); exit = new JMenuItem("Exit"); programConfigBox = new JMenuItem("Ecologia"); simConfigBox = new JMenuItem("Simulation"); genomeConfigBox = new JMenuItem("Genomes"); configFileDialog = new JMenuItem("Configuration file"); help = new JMenuItem("Help"); about = new JMenuItem("About"); menubar.add(file); menubar.add(configuration); menubar.add(help_menu); file.add(new_run); file.add(exit); new_run.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int restart = JOptionPane.showConfirmDialog(null, "Restart now?", "Restart?", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE); if (restart == JOptionPane.OK_OPTION) Ecologia.getInstance().reset(); } }); new_run.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, ActionEvent.ALT_MASK)); exit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int confirm = JOptionPane.showConfirmDialog(null, "Quit Ecologia?", "Quit?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if(confirm == JOptionPane.YES_OPTION){ EcologiaIO.log("Stopping Ecologia."); System.exit(0); } } }); exit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK)); configuration.add(programConfigBox); configuration.add(simConfigBox); configuration.add(genomeConfigBox); configuration.add(configFileDialog); programConfigBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { programConfig.showConfig(); } }); simConfigBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { simulationConfig.showConfig(true); } }); genomeConfigBox.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { genomeConfig.showGenomeConfig(true); } }); configFileDialog.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int returnVal = configChooser.showDialog(null, "Load config file"); if (returnVal == JFileChooser.APPROVE_OPTION) { int restart = JOptionPane.showConfirmDialog(null, "Please note: Loading a config file requires a restart.\nRestart now?", "Restart?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (restart == JOptionPane.YES_OPTION) { World.getInstance().readConfigFile(configChooser. getSelectedFile().getAbsolutePath()); Ecologia.getInstance().reset(); } } } }); help_menu.add(help); help.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { helpWindow.setVisible(true); } }); help.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_H, ActionEvent.ALT_MASK)); help_menu.add(about); about.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { JOptionPane.showMessageDialog(null, "Ecologia "+Ecologia.version+ "\n(c) 2014 - 2016 Daniel Vedder\nLicensed under the GPLv3", "About", JOptionPane.INFORMATION_MESSAGE); } }); this.setJMenuBar(menubar); } /** * Add the information panel at the side */ private void addInformationPanel() { EcologiaIO.debug("GUI: creating information panel."); //Configure the main information panel information = new Box(BoxLayout.Y_AXIS); this.add(information, BorderLayout.EAST); information.setBackground(Color.lightGray); //Add the counters at the top update_counter = new JLabel("Updates: "+ World.getInstance().getTurn()); herbivore_counter = new JLabel("Herbivores: "+ World.getInstance().getHerbivoreCount()); carnivore_counter = new JLabel("Carnivores: "+ World.getInstance().getCarnivoreCount()); generation_counter = new JLabel("Generations: "+World.getInstance().getGeneration()); grass_counter = new JLabel("Grass density: "+World.getInstance().getAverageGrassDensity()); information.add(update_counter); information.add(Box.createVerticalStrut(3)); information.add(herbivore_counter); information.add(Box.createVerticalStrut(3)); information.add(carnivore_counter); information.add(Box.createVerticalStrut(3)); information.add(generation_counter); information.add(Box.createVerticalStrut(3)); information.add(grass_counter); information.add(Box.createVerticalStrut(3)); //Add the event ticker ticker = new JTextArea(); ticker.setEditable(false); ticker.setLineWrap(true); ticker.setWrapStyleWord(true); ticker.setText(" --- Runtime Protocol ---"); scrollticker = new JScrollPane(ticker); scrollticker.setWheelScrollingEnabled(true); information.add(scrollticker); information.add(Box.createVerticalStrut(10)); //Add the humidity chooser Box hum_panel = new Box(BoxLayout.X_AXIS); JLabel humidity = new JLabel("Humidity: "); humidityChooser = new JComboBox<String>(new String[] {Humidity.SATURATION.getString(), Humidity.WET.getString(), Humidity.DRY.getString(), Humidity.DROUGHT.getString(), Humidity.SEVERE_DROUGHT.getString()}); humidityChooser.setMaximumSize(new Dimension(140, 30)); humidityChooser.setSelectedItem(World.getInstance().getHumidity().getString()); hum_panel.add(humidity); hum_panel.add(humidityChooser); information.add(hum_panel); information.add(Box.createVerticalStrut(10)); //Add the "Start/Stop" and "Next" buttons Box buttonPanel = new Box(BoxLayout.X_AXIS); run = new JButton("Start"); //This button starts or pauses the simulation. run.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (World.getInstance().isRunning() == false) { Ecologia.getInstance().startThread(); } else { run.setText("Start"); World.getInstance().setRunning(false); } } }); next = new JButton("Next "); //This button advances the simulation by one update. next.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { Ecologia.getInstance().iterate(); } }); buttonPanel.add(Box.createVerticalStrut(1)); buttonPanel.add(run); buttonPanel.add(Box.createVerticalStrut(1)); buttonPanel.add(next); buttonPanel.add(Box.createVerticalStrut(1)); information.add(buttonPanel); information.add(Box.createVerticalStrut(10)); //Add the simulation speed slider information.add(new JLabel("Simulation speed:")); information.add(Box.createVerticalStrut(3)); speedSlider = new JSlider(0, 1500, 1500-World.getInstance().getTimelapse()); speedSlider.setMajorTickSpacing(300); speedSlider.setMinorTickSpacing(50); speedSlider.setPaintTicks(true); speedSlider.setSnapToTicks(true); information.add(speedSlider); information.add(Box.createVerticalStrut(10)); //Add the "Pause at update:" function Box stopPanel = new Box(BoxLayout.X_AXIS); JLabel stopLabel = new JLabel("Pause at update:"); stopAtField = new JTextField(5); stopAtField.setMaximumSize(stopAtField.getPreferredSize()); stopAtField.setText(Integer.toString(World.getInstance().getStopAt())); stopPanel.add(Box.createVerticalStrut(3)); stopPanel.add(stopLabel); stopPanel.add(Box.createVerticalStrut(1)); stopPanel.add(stopAtField); stopPanel.add(Box.createVerticalStrut(3)); stopPanel.setMaximumSize(stopPanel.getPreferredSize()); information.add(stopPanel); information.add(Box.createVerticalStrut(10)); //Add the disable display check box disableDisplay = new JCheckBox("Freeze display"); information.add(disableDisplay); information.add(Box.createVerticalStrut(10)); } /** * Add the actual display. */ private void addDisplay() { display = new Display(World.getInstance().getSize()); scrollscreen = new JScrollPane(display, JScrollPane. VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); this.add(scrollscreen, BorderLayout.CENTER); } /** * Destroy all windows in preparation for a new run. */ public void reset() { EcologiaIO.debug("Resetting the GUI."); programConfig.dispose(); simulationConfig.dispose(); genomeConfig.dispose(); helpWindow.dispose(); display.getInfoBox().dispose(); this.dispose(); } /** * Display news items on the ticker */ public void displayNews() { EcologiaIO.debug("GUI: updating news."); ArrayList<String> news = World.getInstance().collectNews(); if (!news.isEmpty()) { for (int i = 0; i < news.size(); i++) { ticker.append("\n"+news.get(i)); } World.getInstance().giveNews(null); //reset the news list ticker.setCaretPosition(ticker.getText().length()); //XXX Expensive? } } }