/* * This file is part of MazeSolver. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * Copyright (c) 2014 MazeSolver * Sergio M. Afonso Fumero <theSkatrak@gmail.com> * Kevin I. Robayna Hernández <kevinirobaynahdez@gmail.com> */ /** * @file MainWindow.java * @date 21/10/2014 */ package es.ull.mazesolver.gui; import java.awt.BorderLayout; import java.awt.Desktop; import java.awt.Dimension; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.WindowEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.IOException; import java.net.URI; import java.util.ArrayList; import java.util.Locale; import java.util.Map; import java.util.Observable; import java.util.Observer; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.ButtonGroup; import javax.swing.ImageIcon; import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; 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.JRadioButtonMenuItem; import javax.swing.JSlider; import javax.swing.JSplitPane; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.plaf.basic.BasicSplitPaneUI; import com.alee.laf.WebLookAndFeel; import com.alee.laf.toolbar.WebToolBar; import com.github.rodionmoiseev.c10n.C10N; import com.github.rodionmoiseev.c10n.C10NConfigBase; import com.github.rodionmoiseev.c10n.annotations.DefaultC10NAnnotations; import com.github.rodionmoiseev.c10n.annotations.En; import es.ull.mazesolver.agent.Agent; import es.ull.mazesolver.gui.configuration.AgentConfigurationPanel; import es.ull.mazesolver.gui.environment.Environment; import es.ull.mazesolver.gui.environment.EnvironmentSet; import es.ull.mazesolver.maze.Maze; import es.ull.mazesolver.translations.ButtonTranslations; import es.ull.mazesolver.translations.Languages; import es.ull.mazesolver.translations.MenuTranslations; import es.ull.mazesolver.translations.SimulatorResultTranslations; import es.ull.mazesolver.translations.Translatable; import es.ull.mazesolver.translations.Translations; import es.ull.mazesolver.util.InteractionMode; import es.ull.mazesolver.util.Pair; import es.ull.mazesolver.util.SimulationManager; import es.ull.mazesolver.util.SimulationResults; /** * Ventana principal del programa. Sólo puede haber una, así que implementa el * patrón 'singleton'. */ public class MainWindow extends JFrame implements Observer, Translatable { private static String APP_NAME = "Maze Solver"; private static String VERSION_STRING = "1.2"; private static int DEFAULT_WIDTH = 640; private static int DEFAULT_HEIGHT = 480; private static int MINIMUM_ZOOM_VAL = 1; private static int MAXIMUM_ZOOM_VAL = 100; private static double MINIMUM_ZOOM_AUG = 1; private static double MAXIMUM_ZOOM_AUG = 10; private static final String USER_DOCS_URL = "https://github.com/MazeSolver/MazeSolver/wiki"; private static final String API_DOCS_URL = "https://mazesolver.github.io/MazeSolver/"; private static final String ISSUES_URL = "https://github.com/MazeSolver/MazeSolver/labels/bug"; private static final long serialVersionUID = 1L; private static MainWindow s_instance; private static Translations s_tr; /** * Inicializa la interfaz gráfica y la muestra por pantalla. * * @param args * No utilizados. */ public static void main (String [] args) { try { UIManager.setLookAndFeel(WebLookAndFeel.class.getCanonicalName()); } catch (Exception e) { } MainWindow wnd = MainWindow.getInstance(); wnd.setTitle(APP_NAME); wnd.setSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT)); wnd.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); wnd.setMinimumSize(new Dimension(DEFAULT_WIDTH, DEFAULT_HEIGHT)); wnd.setLocationRelativeTo(null); wnd.setVisible(true); } /** * Obtiene la instancia única de la clase o la crea si no existe. * * @return Instancia única de la clase. */ public static MainWindow getInstance () { if (s_instance == null) s_instance = new MainWindow(); return s_instance; } /** * Obtiene la instancia de traductor de la aplicación o la crea si no había * sido creada antes. * * @return El traductor de la aplicación. */ public static Translations getTranslations () { if (s_tr == null) s_tr = C10N.get(Translations.class); return s_tr; } // Panel que contiene tanto el panel con los laberintos como el panel de // configuración, de tal manera que se pueden cambiar de tamaño. private JSplitPane m_split_panel; // Barra de menús private JMenuBar m_menu_bar; // Barra de acciones private WebToolBar m_toolbar; // Panel de configuración. Por defecto está oculto, pero cuando el usuario // vaya a configurar un agente la interfaz adecuada aparecerá en su lugar. private AgentConfigurationPanel m_config_panel; // Otros elementos en la interfaz private JLabel m_zoom_lb; private JButton m_run, m_step, m_pause, m_stop; private JSlider m_zoom; private JMenu m_menu_file, m_menu_env, m_menu_agent, m_menu_sim, m_menu_config, m_menu_language, m_menu_help; private JMenuItem m_itm_exit; private JMenuItem m_itm_env_new, m_itm_env_save, m_itm_env_config, m_itm_env_close; private JMenuItem m_itm_agent_new, m_itm_agent_open, m_itm_agent_save, m_itm_agent_copy, m_itm_agent_config, m_itm_agent_remove; private JMenuItem m_itm_sim_run, m_itm_sim_step, m_itm_sim_pause, m_itm_sim_stop, m_itm_sim_fastrun; private JRadioButtonMenuItem m_itm_mode_sim, m_itm_mode_edit; private JMenuItem m_itm_language_spanish, m_itm_language_english, m_itm_language_german, m_itm_language_russian, m_itm_language_french, m_itm_language_italian, m_itm_language_slovenian, m_itm_language_romanian, m_itm_language_greek; private JMenuItem m_itm_userdoc, m_itm_apidoc, m_itm_bugs, m_itm_about; // Representación del modelo private EnvironmentSet m_environments; private SimulationManager m_simulation; // Interacción con el usuario private LoggingConsole m_console; private Agent m_agent_clipboard; /** * Constructor de la clase. Crea la interfaz y configura su estado interno * para permitir el uso del programa. */ private MainWindow () { C10N.configure(new C10NConfigBase() { @Override protected void configure () { // install default annotations install(new DefaultC10NAnnotations()); bindBundle("translations.Translations"); /** * setup c10n to fallback to @En annotation when locale does not match * any other registered annotation, by binding it without specifying the * locale. */ bindAnnotation(En.class); } }); // Lo llamamos para asegurarnos de que las traducciones están cargadas antes // de crear los mensajes de error. MainWindow.getTranslations(); createInterface(); m_simulation = new SimulationManager(m_environments); m_simulation.addObserver(this); // Una vez todo está creado traducimos la aplicación translate(); } /** * Construye los objetos de los que se compone la interfaz. */ private void createInterface () { setLayout(new BorderLayout()); createMenus(); createToolbar(); JPanel global_panel = new JPanel(new BorderLayout()); global_panel.add(m_toolbar, BorderLayout.NORTH); m_environments = new EnvironmentSet(); m_split_panel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true, null, m_environments); m_console = new LoggingConsole(); final JSplitPane console_split = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, m_split_panel, m_console); m_console.addPropertyChangeListener("ConsoleDisplay", new PropertyChangeListener() { @Override public void propertyChange (PropertyChangeEvent e) { // Si antes estaba visible, ahora no lo está, así que ajustamos el // separador para que se minimice la consola if ((Boolean) e.getOldValue()) console_split.resetToPreferredSizes(); // Si no, redimensionamos el panel para mostrar la consola else console_split.setDividerLocation(0.75); } }); console_split.setResizeWeight(1); global_panel.add(console_split, BorderLayout.CENTER); add(m_menu_bar, BorderLayout.NORTH); add(global_panel, BorderLayout.CENTER); // Creamos las acciones de Ctrl+C y Ctrl+V para copiar agentes y enlazamos // la tecla "delete" para eliminar el agente seleccionado InputMap env_input = m_environments.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); ActionMap env_action = m_environments.getActionMap(); env_input.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, InputEvent.CTRL_DOWN_MASK), "AgentCopy"); env_input.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, InputEvent.CTRL_DOWN_MASK), "AgentPaste"); env_input.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "AgentDelete"); env_action.put("AgentCopy", new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed (ActionEvent e) { m_agent_clipboard = null; Environment env = m_environments.getSelectedEnvironment(); if (env != null) m_agent_clipboard = env.getSelectedAgent(); } }); env_action.put("AgentPaste", new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed (ActionEvent e) { Environment env = m_environments.getSelectedEnvironment(); if (env != null && m_agent_clipboard != null) env.addAgent((Agent) m_agent_clipboard.clone()); } }); env_action.put("AgentDelete", new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed (ActionEvent e) { Environment env = m_environments.getSelectedEnvironment(); if (env != null) { Agent agent = env.getSelectedAgent(); if (agent != null) env.removeAgent(agent); } } }); closeConfigurationPanel(); pack(); } /** * Crea la barra de menús y sus elementos. */ private void createMenus () { m_menu_bar = new JMenuBar(); m_menu_file = new JMenu(); m_menu_env = new JMenu(); m_menu_agent = new JMenu(); m_menu_sim = new JMenu(); m_menu_config = new JMenu(); m_menu_language = new JMenu(); m_menu_help = new JMenu(); // Menú "File" m_itm_exit = new JMenuItem(); m_menu_file.add(m_itm_exit); // Menú "Environment" m_itm_env_new = new JMenuItem(); m_itm_env_config = new JMenuItem(); m_itm_env_save = new JMenuItem(); m_itm_env_close = new JMenuItem(); m_menu_env.add(m_itm_env_new); m_menu_env.add(m_itm_env_config); m_menu_env.addSeparator(); m_menu_env.add(m_itm_env_save); m_menu_env.addSeparator(); m_menu_env.add(m_itm_env_close); // Menú "Agent" m_itm_agent_new = new JMenuItem(); m_itm_agent_config = new JMenuItem(); m_itm_agent_copy = new JMenuItem(); m_itm_agent_open = new JMenuItem(); m_itm_agent_save = new JMenuItem(); m_itm_agent_remove = new JMenuItem(); m_menu_agent.add(m_itm_agent_new); m_menu_agent.add(m_itm_agent_config); m_menu_agent.add(m_itm_agent_copy); m_menu_agent.addSeparator(); m_menu_agent.add(m_itm_agent_open); m_menu_agent.add(m_itm_agent_save); m_menu_agent.addSeparator(); m_menu_agent.add(m_itm_agent_remove); // Menú "Simulation" m_itm_sim_run = new JMenuItem(); m_itm_sim_step = new JMenuItem(); m_itm_sim_pause = new JMenuItem(); m_itm_sim_stop = new JMenuItem(); m_itm_sim_fastrun = new JMenuItem(); m_menu_sim.add(m_itm_sim_run); m_menu_sim.add(m_itm_sim_step); m_menu_sim.add(m_itm_sim_pause); m_menu_sim.add(m_itm_sim_stop); m_menu_sim.addSeparator(); m_menu_sim.add(m_itm_sim_fastrun); // Menú "Configuration" m_itm_mode_sim = new JRadioButtonMenuItem(); m_itm_mode_edit = new JRadioButtonMenuItem(); m_itm_mode_sim.setSelected(true); m_menu_config.add(m_itm_mode_sim); m_menu_config.add(m_itm_mode_edit); ButtonGroup group = new ButtonGroup(); group.add(m_itm_mode_sim); group.add(m_itm_mode_edit); // Menú "Language" m_itm_language_spanish = new JMenuItem(); m_itm_language_english = new JMenuItem(); m_itm_language_german = new JMenuItem(); m_itm_language_russian = new JMenuItem(); m_itm_language_french = new JMenuItem(); m_itm_language_italian = new JMenuItem(); m_itm_language_slovenian = new JMenuItem(); m_itm_language_romanian = new JMenuItem(); m_itm_language_greek = new JMenuItem(); m_menu_language.add(m_itm_language_spanish); m_menu_language.add(m_itm_language_english); m_menu_language.add(m_itm_language_german); m_menu_language.add(m_itm_language_russian); m_menu_language.add(m_itm_language_french); m_menu_language.add(m_itm_language_italian); m_menu_language.add(m_itm_language_slovenian); m_menu_language.add(m_itm_language_romanian); m_menu_language.add(m_itm_language_greek); // Menú "Help" ImageIcon world = new ImageIcon(getClass().getResource("/images/world.png")); ImageIcon info = new ImageIcon(getClass().getResource("/images/info.png")); m_itm_userdoc = new JMenuItem("", world); m_itm_apidoc = new JMenuItem("", world); m_itm_bugs = new JMenuItem("", world); m_itm_about = new JMenuItem("", info); m_menu_help.add(m_itm_userdoc); m_menu_help.add(m_itm_apidoc); m_menu_help.add(m_itm_bugs); m_menu_help.addSeparator(); m_menu_help.add(m_itm_about); m_menu_bar.add(m_menu_file); m_menu_bar.add(m_menu_env); m_menu_bar.add(m_menu_agent); m_menu_bar.add(m_menu_sim); m_menu_bar.add(m_menu_config); m_menu_bar.add(m_menu_language); m_menu_bar.add(m_menu_help); setupMenuListeners(); } /** * Crea la barra de acciones. */ private void createToolbar () { m_toolbar = new WebToolBar(); m_toolbar.setFloatable(false); m_run = new JButton(); m_step = new JButton(); m_pause = new JButton(); m_stop = new JButton(); m_zoom = new JSlider(MINIMUM_ZOOM_VAL, MAXIMUM_ZOOM_VAL); m_zoom_lb = new JLabel(); m_pause.setEnabled(false); m_stop.setEnabled(false); m_zoom.setValue(MINIMUM_ZOOM_VAL); m_toolbar.add(m_run); m_toolbar.add(m_step); m_toolbar.add(m_pause); m_toolbar.add(m_stop); m_toolbar.addToEnd(m_zoom_lb); m_toolbar.addToEnd(m_zoom); setupToolbarListeners(); } /** * Crea los ActionListeners para los menús. */ private void setupMenuListeners () { // Menú "File" // ///////////////////////////////////////////////////////////////////////// m_itm_exit.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { dispatchEvent(new WindowEvent(MainWindow.getInstance(), WindowEvent.WINDOW_CLOSING)); } }); // Menú "Environment" // ///////////////////////////////////////////////////////////////////////// m_itm_env_new.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { EnvSelectorDialog dialog = new EnvSelectorDialog(MainWindow.getInstance(),m_environments); dialog.setLocationRelativeTo(MainWindow.this); Environment generated = dialog.showDialog(); if (generated != null) m_environments.addEnvironment(generated); } }); m_itm_env_config.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { Environment actual_env = m_environments.getSelectedEnvironment(); if (actual_env == null) { JOptionPane.showMessageDialog(null, s_tr.message().noEnvironmentSelected(), s_tr.message().mazeChangeFailed(), JOptionPane.WARNING_MESSAGE); return; } EnvSelectorDialog dialog = new EnvSelectorDialog(MainWindow.this, m_environments, actual_env); dialog.setLocationRelativeTo(MainWindow.this); Environment new_env = dialog.showDialog(); if (new_env != null) { for (int i = 0; i < actual_env.getAgentCount(); i++) new_env.addAgent(actual_env.getAgent(i)); m_environments.exchangeEnvironments(actual_env, new_env); } } }); m_itm_env_save.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { try { FileDialog.saveMaze(m_environments.getSelectedEnvironment().getMaze()); } catch (IOException exc) { JOptionPane.showMessageDialog(null, exc.getMessage(), s_tr.message().fileSaveFailed(), JOptionPane.ERROR_MESSAGE); } catch (Exception exc) { JOptionPane.showMessageDialog(null, s_tr.message().noEnvironmentSelected(), s_tr .message().fileSaveFailed(), JOptionPane.WARNING_MESSAGE); } } }); m_itm_env_close.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { m_environments.removeSelectedEnvironment(); } }); // Menú "Agent" // ///////////////////////////////////////////////////////////////////////// m_itm_agent_new.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { Environment env = m_environments.getSelectedEnvironment(); if (env == null) { JOptionPane.showMessageDialog(null, s_tr.message().noEnvironmentSelected(), s_tr .message().agentCreationFailed(), JOptionPane.WARNING_MESSAGE); return; } Maze maze = env.getMaze(); AgentSelectorDialog dialog = new AgentSelectorDialog(MainWindow.this, (maze.getHeight() * maze.getWidth()) - env.getAgentCount()); dialog.setLocationRelativeTo(MainWindow.this); Agent [] agents = dialog.showDialog(); if (agents != null) { for (Agent ag: agents) m_environments.addAgentToSelectedEnvironment(ag); } } }); m_itm_agent_open.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { Environment env = m_environments.getSelectedEnvironment(); if (env != null) { try { Agent ag = FileDialog.loadAgent(env); if (ag != null) m_environments.addAgentToSelectedEnvironment(ag); } catch (IOException exc) { JOptionPane.showMessageDialog(null, exc.getMessage(), s_tr.message().fileOpenFailed(), JOptionPane.ERROR_MESSAGE); } } else { JOptionPane.showMessageDialog(null, s_tr.message().noEnvironmentSelected(), s_tr.message().fileOpenFailed(), JOptionPane.WARNING_MESSAGE); } } }); m_itm_agent_save.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { Environment env = m_environments.getSelectedEnvironment(); if (env != null) { Agent ag = env.getSelectedAgent(); if (ag != null) { try { FileDialog.saveAgent(ag); } catch (IOException exc) { JOptionPane.showMessageDialog(null, exc.getMessage(), s_tr.message().fileSaveFailed(), JOptionPane.ERROR_MESSAGE); } } else { JOptionPane.showMessageDialog(null, s_tr.message().noAgentSelected(), s_tr.message().fileSaveFailed(), JOptionPane.WARNING_MESSAGE); } } else { JOptionPane.showMessageDialog(null, s_tr.message().noEnvironmentSelected(), s_tr.message().fileSaveFailed(), JOptionPane.WARNING_MESSAGE); } } }); m_itm_agent_copy.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { try { Environment env = m_environments.getSelectedEnvironment(); m_environments.addAgentToSelectedEnvironment((Agent) env.getSelectedAgent().clone()); } catch (Exception exc) { JOptionPane.showMessageDialog(null, s_tr.message().noAgentSelected(), s_tr.message().cloningFailed(), JOptionPane.WARNING_MESSAGE); } } }); m_itm_agent_config.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { try { Agent ag = m_environments.getSelectedEnvironment().getSelectedAgent(); setConfigurationPanel(ag.getConfigurationPanel()); } catch (Exception exc) { JOptionPane.showMessageDialog(null, s_tr.message().noAgentSelected(), s_tr.message() .agentConfigFailed(), JOptionPane.WARNING_MESSAGE); } } }); m_itm_agent_remove.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { try { Environment env = m_environments.getSelectedEnvironment(); m_environments.removeAgentFromEnvironment(env.getSelectedAgent(), env); } catch (Exception exc) { JOptionPane.showMessageDialog(null, s_tr.message().noAgentSelected(), s_tr.message() .agentRemovalFailed(), JOptionPane.WARNING_MESSAGE); } } }); // Menú "Simulation" // ///////////////////////////////////////////////////////////////////////// m_itm_sim_run.addActionListener(new RunAction()); m_itm_sim_step.addActionListener(new StepAction()); m_itm_sim_pause.addActionListener(new PauseAction()); m_itm_sim_stop.addActionListener(new StopAction()); m_itm_sim_fastrun.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { FastSimulationDialog dialog = new FastSimulationDialog(MainWindow.this); dialog.setLocationRelativeTo(MainWindow.this); int steps = dialog.showDialog(); if (steps > 0) startFastSimulation(steps); } }); // Menú "Configuration" // ///////////////////////////////////////////////////////////////////////// m_itm_mode_sim.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { setInteractionModeState(InteractionMode.SIMULATION); m_environments.setInteractionMode(InteractionMode.SIMULATION); } }); m_itm_mode_edit.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { setInteractionModeState(InteractionMode.EDITION); m_environments.setInteractionMode(InteractionMode.EDITION); } }); // Menú "Languages" // ///////////////////////////////////////////////////////////////////////// class LanguageAction implements ActionListener { private String m_lang_code; public LanguageAction (String lang_code) { m_lang_code = lang_code; } @Override public void actionPerformed (ActionEvent e) { s_tr = C10N.get(Translations.class, new Locale(m_lang_code)); translate(); } } m_itm_language_spanish.addActionListener(new LanguageAction("es")); m_itm_language_english.addActionListener(new LanguageAction("en")); m_itm_language_german.addActionListener(new LanguageAction("de")); m_itm_language_russian.addActionListener(new LanguageAction("ru")); m_itm_language_french.addActionListener(new LanguageAction("fr")); m_itm_language_italian.addActionListener(new LanguageAction("it")); m_itm_language_slovenian.addActionListener(new LanguageAction("si")); m_itm_language_romanian.addActionListener(new LanguageAction("ro")); m_itm_language_greek.addActionListener(new LanguageAction("el")); // Menú "Help" // ///////////////////////////////////////////////////////////////////////// class URLLauncher implements ActionListener { private String m_url; public URLLauncher (String url) { m_url = url; } @Override public void actionPerformed (ActionEvent e) { try { Desktop.getDesktop().browse(URI.create(m_url)); } catch (Exception exc) { JOptionPane.showMessageDialog(null, exc.getMessage(), s_tr.message().urlLaunchFailed(), JOptionPane.ERROR_MESSAGE); } } } m_itm_userdoc.addActionListener(new URLLauncher(USER_DOCS_URL)); m_itm_apidoc.addActionListener(new URLLauncher(API_DOCS_URL)); m_itm_bugs.addActionListener(new URLLauncher(ISSUES_URL)); m_itm_about.addActionListener(new ActionListener() { @Override public void actionPerformed (ActionEvent e) { JOptionPane .showMessageDialog( null, "<html><h2>" + APP_NAME + " " + VERSION_STRING + "</h2>" + "Copyright © 2014 - 2015<br>Sergio M. Afonso Fumero and Kevin I. Robayna Hernández<br><br>" + "This program is free software: you can redistribute it and/or modify<br>" + "it under the terms of the GNU General Public License as published by<br>" + "the Free Software Foundation, either version 3 of the License, or<br>" + "(at your option) any later version.<br><br>" + "This program is distributed in the hope that it will be useful,<br>" + "but WITHOUT ANY WARRANTY; without even the implied warranty of<br>" + "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the<br>" + "GNU General Public License for more details.<br><br>" + "You should have received a copy of the GNU General Public License<br>" + "along with this program. If not, see <<a href=\"http://www.gnu.org/licenses/\">http://www.gnu.org/licenses/</a>>.</html>", s_tr.menu().about(), JOptionPane.INFORMATION_MESSAGE); } }); } /** * Configura los listeners de los botones en la barra de herramientas. */ private void setupToolbarListeners () { m_run.addActionListener(new RunAction()); m_step.addActionListener(new StepAction()); m_pause.addActionListener(new PauseAction()); m_stop.addActionListener(new StopAction()); m_zoom.addChangeListener(new ChangeListener() { @Override public void stateChanged (ChangeEvent e) { JSlider src = (JSlider) e.getSource(); // Ajuste lineal del zoom double a = (MAXIMUM_ZOOM_AUG - MINIMUM_ZOOM_AUG) / (MAXIMUM_ZOOM_VAL - MINIMUM_ZOOM_VAL); double b = MAXIMUM_ZOOM_AUG - a * MAXIMUM_ZOOM_VAL; m_environments.setZoom(a * src.getValue() + b); } }); } /** * Devuelve una referencia a la consola. * * @return Referencia a la consola de la ventana. */ public LoggingConsole getConsole () { return m_console; } /** * Devuelve una referencia al conjunto de entornos. * * @return Referencia al conjunto de entornos de la ventana. */ public EnvironmentSet getEnvironments () { return m_environments; } /** * Abre el panel de configuración. * * @param ag_panel * Panel de configuración que se quiere abrir. */ public void setConfigurationPanel (final AgentConfigurationPanel ag_panel) { if (m_config_panel != null) closeConfigurationPanel(); if (ag_panel != null) { ((BasicSplitPaneUI) m_split_panel.getUI()).getDivider().setVisible(true); m_config_panel = ag_panel; ag_panel.addEventListener(new AgentConfigurationPanel.EventListener() { @Override public void onSuccess (ArrayList <String> msgs) { for (String msg: msgs) m_console.writeInfo(msg); closeConfigurationPanel(); } @Override public void onError (ArrayList <String> errors) { for (String error: errors) m_console.writeError(error); } @Override public void onCancel () { closeConfigurationPanel(); } }); m_split_panel.add(m_config_panel); revalidate(); repaint(); m_itm_env_close.setEnabled(false); m_itm_agent_remove.setEnabled(false); } } /** * Cierra el panel de configuración. */ public void closeConfigurationPanel () { ((BasicSplitPaneUI) m_split_panel.getUI()).getDivider().setVisible(false); if (m_config_panel != null) { m_split_panel.remove(m_config_panel); m_config_panel = null; revalidate(); repaint(); } m_itm_env_close.setEnabled(true); m_itm_agent_remove.setEnabled(true); } /* * (non-Javadoc) * * @see java.util.Observer#update(java.util.Observable, java.lang.Object) */ @Override public void update (Observable obs, Object obj) { SimulatorResultTranslations tr_sim = s_tr.simulation(); // La notificación viene del comienzo de la ejecución if (m_simulation.isRunning()) setSimulatingState(); // La notificación viene de una pausa en la ejecución else if (m_simulation.isPaused()) setPausedContinuedState(); // La notificación viene de la parada o finalización de la ejecución else { stopSimulation(); SimulationResults results = (SimulationResults) obj; ArrayList <Environment> envs = m_environments.getEnvironmentList(); ArrayList <Maze> mazes = new ArrayList <Maze>(); for (Environment env: envs) { if (!mazes.contains(env.getMaze())) mazes.add(env.getMaze()); } m_console.writeInfo(tr_sim.title()); m_console.writeInfo("=================="); for (int i = 0; i < mazes.size(); i++) { Maze maze = mazes.get(i); Agent maze_winner = results.getWinner(maze); m_console.writeInfo("=== " + tr_sim.maze() + " " + (i + 1) + " (" + maze.getWidth() + "x" + maze.getHeight() + ") ==="); m_console.writeInfo("* " + tr_sim.timeTakenFirst() + ": " + results.timeTakenFirst(maze)); m_console.writeInfo("* " + tr_sim.timeTakenLast() + ": " + results.timeTakenLast(maze)); m_console.writeInfo("* " + tr_sim.winner() + ": " + (maze_winner != null? maze_winner.getAgentName() : tr_sim.none())); m_console.writeInfo(""); for (int j = 0; j < envs.size(); j++) { Environment env = envs.get(j); if (env.getMaze() == maze) { Agent env_winner = results.getWinner(env); m_console.writeInfo(" == " + env.getTitle() + " =="); m_console.writeInfo("* " + tr_sim.timeTakenFirst() + ": " + results.timeTakenFirst(env)); m_console.writeInfo("* " + tr_sim.timeTakenLast() + ": " + results.timeTakenLast(env)); m_console.writeInfo("* " + tr_sim.winner() + ": " + (env_winner != null? env_winner.getAgentName() : tr_sim.none())); m_console.writeInfo(""); m_console.writeInfo(" * " + tr_sim.agentsDetail() + ":"); for (Map.Entry <Agent, Pair<Integer, Integer>> entry: results.getSteps(env).entrySet()) { Agent ag = entry.getKey(); String finished = maze.containsPoint(new Point(ag.getX(), ag.getY()))? tr_sim.notFinished() : tr_sim .finished(); m_console.writeInfo(" - " + ag.getAgentName() + ": " + entry.getValue().first + " " + tr_sim.steps() + " / " + entry.getValue().second + " " + tr_sim.iterations() + " [" + finished + "]"); } } } } m_console.writeInfo("=================="); } } /* * (non-Javadoc) * * @see es.ull.mazesolver.translations.Translatable#translate() */ @Override public void translate () { MenuTranslations m_tr = s_tr.menu(); ButtonTranslations b_tr = s_tr.button(); Languages m_lang_tr = s_tr.languages(); m_menu_file.setText(m_tr.file()); m_menu_env.setText(m_tr.environment()); m_menu_agent.setText(m_tr.agent()); m_menu_sim.setText(m_tr.simulation()); m_menu_config.setText(m_tr.configuration()); m_menu_language.setText(m_tr.language()); m_menu_help.setText(m_tr.help()); m_itm_exit.setText(m_tr.exit()); m_itm_env_new.setText(m_tr.newEnv() + "..."); m_itm_env_config.setText(m_tr.configEnv() + "..."); m_itm_env_save.setText(m_tr.saveMaze() + "..."); m_itm_env_close.setText(m_tr.closeEnv()); m_itm_agent_new.setText(m_tr.newAgent() + "..."); m_itm_agent_open.setText(m_tr.openAgent() + "..."); m_itm_agent_save.setText(m_tr.saveAgent() + "..."); m_itm_agent_copy.setText(m_tr.copyAgent()); m_itm_agent_config.setText(m_tr.configureAgent() + "..."); m_itm_agent_remove.setText(m_tr.removeAgent()); m_itm_sim_run.setText(b_tr.run()); m_itm_sim_step.setText(b_tr.step()); m_itm_sim_stop.setText(b_tr.stop()); m_itm_sim_fastrun.setText(b_tr.fastRun() + "..."); m_itm_mode_sim.setText(m_tr.simulationMode()); m_itm_mode_edit.setText(m_tr.editMode()); m_itm_language_spanish.setText(m_lang_tr.spanish()); m_itm_language_english.setText(m_lang_tr.english()); m_itm_language_german.setText(m_lang_tr.german()); m_itm_language_russian.setText(m_lang_tr.russian()); m_itm_language_french.setText(m_lang_tr.french()); m_itm_language_italian.setText(m_lang_tr.italian()); m_itm_language_slovenian.setText(m_lang_tr.slovenian()); m_itm_language_romanian.setText(m_lang_tr.romanian()); m_itm_language_greek.setText(m_lang_tr.greek()); m_itm_userdoc.setText(m_tr.userDocs()); m_itm_apidoc.setText(m_tr.apiDocs()); m_itm_bugs.setText(m_tr.knownProblems()); m_itm_about.setText(m_tr.about() + "..."); m_run.setText(b_tr.run()); m_step.setText(b_tr.step()); m_stop.setText(b_tr.stop()); m_zoom_lb.setText(b_tr.zoom() + ":"); String pauseText = b_tr.kontinue(); if (m_simulation.isRunning() || m_simulation.isStopped()) pauseText = b_tr.pause(); m_pause.setText(pauseText); m_itm_sim_pause.setText(pauseText); m_console.translate(); if (m_config_panel != null) m_config_panel.translate(); } /** * Adapta los menús al estado de "Simulación en curso" y la comienza. */ private void startSimulation () { //setSimulatingState(); m_simulation.startSimulation(); } /** * Adapta los menús al estado de "Simulación en curso" comienza una simulación * rápida. * @param steps * Número máximo de pasos que se van a simular. */ private void startFastSimulation (int steps) { if (m_simulation.isPaused()) { m_simulation.startFastSimulation(steps); setPausedContinuedState(); } else { setSimulatingState(); m_simulation.startFastSimulation(steps); } } /** * Pausa o reanuda la simulación dependiendo de su estado y mantiene coherente * el estado de los menús. */ private void pauseSimulation () { if (m_simulation.isPaused()) m_simulation.startSimulation(); else m_simulation.pauseSimulation(); setPausedContinuedState(); } /** * Para la simulación y vuelve a dejar los menús en su estado inicial. */ private void stopSimulation () { setStoppedState(); m_simulation.stopSimulation(); for (Environment env: m_environments.getEnvironmentList()) { for (int i = 0; i < env.getAgentCount(); i++) env.getAgent(i).resetMemory(); } } /** * Adapta los menús al estado de "Simulación en curso". */ private void setSimulatingState () { // Desactivamos los menús que no se pueden utilizar durante la simulación m_itm_env_new.setEnabled(false); m_itm_env_config.setEnabled(false); m_run.setEnabled(false); m_step.setEnabled(false); m_pause.setEnabled(true); m_stop.setEnabled(true); m_itm_sim_run.setEnabled(false); m_itm_sim_step.setEnabled(false); m_itm_sim_pause.setEnabled(true); m_itm_sim_stop.setEnabled(true); m_itm_mode_edit.setEnabled(false); } /** * Adapta los menús al estado de "Simulación pausada" o "Simulación en curso" * dependiendo de si la simulación está pausada o en curso. */ private void setPausedContinuedState () { if (m_simulation.isPaused()) { m_pause.setText(s_tr.button().kontinue()); m_step.setEnabled(true); m_itm_sim_pause.setText(s_tr.button().kontinue()); m_itm_sim_step.setEnabled(true); } else { m_pause.setText(s_tr.button().pause()); m_step.setEnabled(false); m_itm_sim_pause.setText(s_tr.button().pause()); m_itm_sim_step.setEnabled(false); } } /** * Adapta los menús al estado de "Simulación parada". */ private void setStoppedState () { m_itm_env_new.setEnabled(true); m_itm_env_config.setEnabled(true); m_pause.setText(s_tr.button().pause()); m_run.setEnabled(true); m_step.setEnabled(true); m_pause.setEnabled(false); m_stop.setEnabled(false); m_itm_sim_pause.setText(s_tr.button().pause()); m_itm_sim_run.setEnabled(true); m_itm_sim_step.setEnabled(true); m_itm_sim_pause.setEnabled(false); m_itm_sim_stop.setEnabled(false); m_itm_mode_edit.setEnabled(true); } /** * Adapta los menús dependiendo del modo de interacción al que se esté * cambiando. * * @param mode * Nuevo modo de interacción. */ private void setInteractionModeState (InteractionMode mode) { boolean enable = false; switch (mode) { case SIMULATION: enable = true; break; case EDITION: enable = false; break; } m_run.setEnabled(enable); m_step.setEnabled(enable); m_menu_sim.setEnabled(enable); m_menu_agent.setEnabled(enable); } private class RunAction implements ActionListener { @Override public void actionPerformed (ActionEvent e) { startSimulation(); } } private class StepAction implements ActionListener { @Override public void actionPerformed (ActionEvent e) { m_simulation.stepSimulation(); } } private class PauseAction implements ActionListener { @Override public void actionPerformed (ActionEvent e) { pauseSimulation(); } } private class StopAction implements ActionListener { @Override public void actionPerformed (ActionEvent e) { stopSimulation(); } } }