/*
* 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 MazeSelectorDialog.java
* @date 14/11/2014
*/
package es.ull.mazesolver.gui;
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSpinner;
import javax.swing.SpinnerNumberModel;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import es.ull.mazesolver.maze.Maze;
import es.ull.mazesolver.maze.MazeCreationAlgorithm;
import es.ull.mazesolver.maze.algorithm.AldousBroder;
import es.ull.mazesolver.maze.algorithm.HuntAndKill;
import es.ull.mazesolver.maze.algorithm.Kruskal;
import es.ull.mazesolver.maze.algorithm.Prim;
import es.ull.mazesolver.maze.algorithm.RecursiveBacktracking;
import es.ull.mazesolver.maze.algorithm.RecursiveDivision;
import es.ull.mazesolver.maze.algorithm.Wilson;
import es.ull.mazesolver.translations.Translations;
/**
* Interfaz gráfica para seleccionar el generador de laberintos y el tamaño del
* laberinto que se desea generar.
*/
public class MazeSelectorDialog extends JDialog {
private static final long serialVersionUID = 1L;
private static final int MIN_MAZE_SIZE = 10;
private static final int MAX_MAZE_SIZE = 100;
private JButton m_ok, m_cancel;
private JComboBox <String> m_algorithms;
private JSpinner m_rows, m_columns;
private ButtonGroup m_type;
private JRadioButton m_perfect, m_add_cycles, m_add_components;
private JSpinner m_cycles, m_components;
private Maze m_result;
/**
* Crea el diálogo de creación de laberintos.
*
* @param parent Ventana padre del diálogo.
*/
public MazeSelectorDialog (Window parent) {
super(parent);
String [] algos =
{"Aldous Broder", "Hunt and Kill", "Kruskal", "Prim", "Recursive Backtracking",
"Recursive Division", "Wilson"};
m_algorithms = new JComboBox <String>(algos);
buildInterface();
setupListeners();
setResizable(false);
setModal(true);
}
/**
* Muestra el diálogo y devuelve el laberinto creado utilizando la
* configuración elegida por el usuario.
*
* @return El laberinto creado o {@code null} si el usuario cancela la
* creación del laberinto.
*/
public Maze showDialog () {
setVisible(true);
return m_result;
}
/**
* Crea los elementos y layouts de la interfaz gráfica.
*/
private void buildInterface () {
Translations tr = MainWindow.getTranslations();
setTitle(tr.maze().createNewMaze());
setLayout(new BorderLayout());
JPanel basic_global = new JPanel(new BorderLayout(5, 5));
JPanel basic_labels = new JPanel(new GridLayout(3, 1, 5, 5));
JPanel basic_controls = new JPanel(new GridLayout(3, 1, 5, 5));
m_rows = new JSpinner(new SpinnerNumberModel(MIN_MAZE_SIZE, MIN_MAZE_SIZE, MAX_MAZE_SIZE, 1));
m_columns =
new JSpinner(new SpinnerNumberModel(MIN_MAZE_SIZE, MIN_MAZE_SIZE, MAX_MAZE_SIZE, 1));
basic_labels.add(new JLabel(tr.maze().algorithm() + ":"));
basic_controls.add(m_algorithms);
basic_labels.add(new JLabel(tr.maze().rows() + ":"));
basic_controls.add(m_rows);
basic_labels.add(new JLabel(tr.maze().columns() + ":"));
basic_controls.add(m_columns);
basic_global.add(basic_labels, BorderLayout.WEST);
basic_global.add(basic_controls, BorderLayout.CENTER);
basic_global.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2, 5,
0, 5), BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), tr.maze().basicConfiguration())));
JPanel adv_global = new JPanel(new BorderLayout());
JPanel adv_labels = new JPanel(new GridLayout(3, 1));
JPanel adv_controls = new JPanel(new GridLayout(3, 1));
m_type = new ButtonGroup();
m_perfect = new JRadioButton(tr.maze().perfectMaze(), true);
m_perfect.setActionCommand("Perfect");
m_add_cycles = new JRadioButton(tr.maze().addCycles() + ":");
m_add_cycles.setActionCommand("Cycles");
m_add_components = new JRadioButton(tr.maze().addWalls() + ":");
m_add_components.setActionCommand("Walls");
m_type.add(m_perfect);
m_type.add(m_add_cycles);
m_type.add(m_add_components);
m_cycles =
new JSpinner(new SpinnerNumberModel(0, 0, (int) Math.round(Maze.perfectMazeWalls(
MIN_MAZE_SIZE, MIN_MAZE_SIZE) * 0.75), 1));
m_components =
new JSpinner(new SpinnerNumberModel(0, 0, (int) Math.round(Maze.perfectMazeEdges(
MIN_MAZE_SIZE, MIN_MAZE_SIZE) * 0.75), 1));
m_cycles.setEnabled(false);
m_components.setEnabled(false);
adv_labels.add(m_perfect);
adv_controls.add(Box.createGlue());
adv_labels.add(m_add_cycles);
adv_controls.add(m_cycles);
adv_labels.add(m_add_components);
adv_controls.add(m_components);
adv_global.add(adv_labels, BorderLayout.WEST);
adv_global.add(adv_controls, BorderLayout.CENTER);
adv_global.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2, 5,
0, 5), BorderFactory.createTitledBorder(
BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), tr.maze().mazeType())));
m_ok = new JButton(tr.button().ok());
m_cancel = new JButton(tr.button().cancel());
JPanel button_panel = new JPanel(new FlowLayout(FlowLayout.RIGHT));
button_panel.add(m_ok);
button_panel.add(m_cancel);
JPanel global = new JPanel(new BorderLayout());
global.add(basic_global, BorderLayout.CENTER);
global.add(adv_global, BorderLayout.SOUTH);
add(global, BorderLayout.CENTER);
add(button_panel, BorderLayout.SOUTH);
pack();
}
/**
* Configura los controladores de la interfaz gráfica.
*/
private void setupListeners () {
ChangeListener dim_change_listener = new ChangeListener() {
@Override
public void stateChanged (ChangeEvent e) {
int rows = (Integer) m_rows.getValue();
int columns = (Integer) m_columns.getValue();
int max_cycles = (int) Math.round(Maze.perfectMazeWalls(rows, columns) * 0.75);
int max_components = (int) Math.round(Maze.perfectMazeEdges(rows, columns) * 0.75);
SpinnerNumberModel cycles_model = (SpinnerNumberModel) m_cycles.getModel();
SpinnerNumberModel components_model = (SpinnerNumberModel) m_components.getModel();
cycles_model.setMaximum(max_cycles);
components_model.setMaximum(max_components);
cycles_model.setValue(Math.min((Integer) cycles_model.getValue(), max_cycles));
components_model.setValue(Math.min((Integer) components_model.getValue(), max_components));
}
};
m_rows.addChangeListener(dim_change_listener);
m_columns.addChangeListener(dim_change_listener);
ActionListener types_listener = new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
m_cycles.setEnabled(e.getSource() == m_add_cycles);
m_components.setEnabled(e.getSource() == m_add_components);
}
};
m_perfect.addActionListener(types_listener);
m_add_cycles.addActionListener(types_listener);
m_add_components.addActionListener(types_listener);
m_ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
String alg_name = (String) m_algorithms.getSelectedItem();
int rows = (Integer) m_rows.getValue();
int columns = (Integer) m_columns.getValue();
MazeCreationAlgorithm alg = null;
switch (alg_name) {
case "Aldous Broder":
alg = new AldousBroder(rows, columns);
break;
case "Hunt and Kill":
alg = new HuntAndKill(rows, columns);
break;
case "Kruskal":
alg = new Kruskal(rows, columns);
break;
case "Prim":
alg = new Prim(rows, columns);
break;
case "Recursive Division":
alg = new RecursiveDivision(rows, columns);
break;
case "Recursive Backtracking":
alg = new RecursiveBacktracking(rows, columns);
break;
case "Wilson":
alg = new Wilson(rows, columns);
break;
}
if (alg != null) {
String command = m_type.getSelection().getActionCommand();
if (command.equals(m_add_cycles.getActionCommand()))
alg.setCycles((Integer) m_cycles.getValue());
else if (command.equals(m_add_components.getActionCommand()))
alg.setComponents((Integer) m_components.getValue() + 1);
m_result = new Maze(alg);
}
setVisible(false);
dispose();
}
});
m_cancel.addActionListener(new ActionListener() {
@Override
public void actionPerformed (ActionEvent e) {
setVisible(false);
dispose();
}
});
}
}