package view; import model.shapes.AShapeFactory; import model.shapes.IShape; import model.shapes.NullShape; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.geom.AffineTransform; import java.lang.reflect.Constructor; /** * This class represents the "view" for the Model-View-Controller architecture * used by the Shapes application. * The communication to the the model is accomplished via two adapters. */ public class ShapesGUI { /** * The GUI component that holds all other GUI components. */ private JApplet _applet; /** * The adapter used to communicate with the model and control the game. */ private ISettingsAdapter _settingsAdapter = new ISettingsAdapter() { // Null-Object Pattern public void changeShape(AShapeFactory factory) { // do nothing } public String[] getShapeClassNames() { return new String[0]; } }; /** * The adapter used to communicate with the model to tell it to paint the * body parts. */ private IPaintAdapter _paintAdapter = new IPaintAdapter() { // Null-Object Pattern public void paint(Graphics g) { // do nothing } }; /** * Factory to use. */ private AShapeFactory _factory = new NullShape().makeFactory(); /** * The JPanel that holds the controls. */ private JPanel _controlPanel = new JPanel(); /** * A panel derived from a JPanel where the shape is drawn. */ private JPanel _displayPanel = new JPanel() { public void paintComponent(Graphics g) { // called whenever the panel is repainted. Graphics2D g2 = (Graphics2D)g; super.paintComponent(g); // do whatever usually is done, e.g. clear the panel. g.setColor(Color.black); // set the drawing color Graphics2D shapeGraphics = (Graphics2D)g2.create(); AffineTransform oldTransform = shapeGraphics.getTransform(); shapeGraphics.translate(getSize().getWidth()/2,getSize().getHeight()/2); _paintAdapter.paint(shapeGraphics); // Delegate to the adapter to get the shape drawn. shapeGraphics.setTransform(oldTransform); } }; /** * The JLabel that labels the factory selection box. */ private JLabel _factoryLbl = new JLabel(); /** * The input text field where the user's guess is typed. */ private JComboBox _factoryChooser = new JComboBox(); /** * Panel to contain the factory label and chooser. */ private JPanel _factoryGroupPanel = new JPanel(); /** * The JPanel that holds the factory settings. */ private JPanel _settingsPanel = new JPanel(); /** * The JLabel that labels the settings. */ private JLabel _settingsLbl = new JLabel(); /** * Panel to contain the settings label and chooser. */ private JPanel _settingsGroupPanel = new JPanel(); /** * The button that when clicked, will change the shape. */ private JButton _changeBtn = new JButton(); /** * Initializes the GUI components. * @param a the applet that holds all GUI components. * @param sa ISettingsAdapter object used to tell the model to change settings. * @param pa IPaintAdapter object used to tell the model to paint. */ public ShapesGUI(JApplet a, ISettingsAdapter sa, IPaintAdapter pa) { _applet = a; _settingsAdapter = sa; _paintAdapter = pa; jbInit(); } /** * Initialize the GUI components. */ private void jbInit() { Container contentPane = _applet.getContentPane(); contentPane.setLayout(new BorderLayout()); _applet.setSize(400, 350); _factoryLbl.setText("Shape:"); _settingsLbl.setText("Settings:"); _changeBtn.setText("Change"); _changeBtn.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(ActionEvent e) { change(); } }); _displayPanel.setBackground(Color.white); _factoryChooser.setEditable(true); _factoryChooser.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { factorySelected(); change(); } }); // add factories String[] factoryClassNames = _settingsAdapter.getShapeClassNames(); for(int i = 0; i < factoryClassNames.length; i++) { try { Class shapeClass = Class.forName(factoryClassNames[i]); Constructor shapeCtor = shapeClass.getConstructor(new Class[]{}); IShape shape = (IShape)shapeCtor.newInstance(new Object[]{}); final AShapeFactory shapeFactory = shape.makeFactory(); ConcreteFactoryChoice choice = new ConcreteFactoryChoice(this, shapeFactory); _factoryChooser.addItem(choice); } catch(Exception e) { } } // add "Add..." choice _factoryChooser.addItem(new AddFactoryChoice(this)); // change factory and shape factorySelected(); change(); _factoryGroupPanel.setLayout(new BorderLayout()); _factoryGroupPanel.add(_factoryLbl,BorderLayout.NORTH); _factoryGroupPanel.add(_factoryChooser,BorderLayout.SOUTH); _settingsGroupPanel.setLayout(new BorderLayout()); _settingsGroupPanel.add(_settingsLbl,BorderLayout.NORTH); _settingsGroupPanel.add(_settingsPanel,BorderLayout.CENTER); _controlPanel.setPreferredSize(new Dimension(200,350)); _controlPanel.setLayout(new BorderLayout()); _controlPanel.add(_factoryGroupPanel,BorderLayout.NORTH); _controlPanel.add(_settingsGroupPanel,BorderLayout.CENTER); _controlPanel.add(_changeBtn,BorderLayout.SOUTH); contentPane.add(_controlPanel, BorderLayout.EAST); contentPane.add(_displayPanel, BorderLayout.CENTER); } /** * Change the shape in the model. */ private void change() { _settingsAdapter.changeShape(_factory); _applet.repaint(); } /** * Change the factory used. */ public void factorySelected() { // first try to use selection as click on existing item try { IFactoryChoice factoryChoice = (IFactoryChoice)_factoryChooser.getSelectedItem(); factoryChoice.select(); } catch(ClassCastException e) { // then try to use selection as new string String factoryClassName = (String)_factoryChooser.getSelectedItem(); addFactoryToChooser(factoryClassName); } _applet.validate(); // force immediate validation } /** * Remove factory settings from dialog. */ private void removeFactorySettings() { if(_factory != null) { _settingsPanel.remove(_factory); _settingsPanel.repaint(); _factory = null; } } /** * Add factory settings to dialog. * @param factory factory to add */ private void addFactorySettings(AShapeFactory factory) { _factory = factory; _settingsPanel.add(_factory); _settingsPanel.repaint(); } /** * Add a factory with the specified class name to the chooser. * @param className class to add */ private void addFactoryToChooser(String className) { for (int i=0; i<_factoryChooser.getItemCount(); ++i) { if (className.equals(_factoryChooser.getItemAt(i).toString())) { // already exists return; } } try { Class shapeClass = Class.forName(className); Constructor shapeCtor = shapeClass.getConstructor(new Class[]{}); IShape shape = (IShape)shapeCtor.newInstance(new Object[]{}); final AShapeFactory shapeFactory = shape.makeFactory(); ConcreteFactoryChoice choice = new ConcreteFactoryChoice(this, shapeFactory); _factoryChooser.insertItemAt(choice, _factoryChooser.getItemCount() - 1); _factoryChooser.setSelectedItem(choice); choice.select(); } catch(Exception e) { } } /** * Nested interface for factory choices. */ private interface IFactoryChoice { /** * Select this choice. */ public void select(); } /** * Nested class for choosing a concrete factory. */ private static class ConcreteFactoryChoice implements IFactoryChoice { /** * GUI used. */ private ShapesGUI _gui; /** * Factory used. */ private AShapeFactory _factory; /** * Make a new concrete factory choice. * @param gui Shapes GUI * @param factory shape factory */ public ConcreteFactoryChoice(ShapesGUI gui, AShapeFactory factory) { _gui = gui; _factory = factory; } /** * Select this choice. */ public void select() { _gui._changeBtn.setEnabled(true); _gui.removeFactorySettings(); _gui.addFactorySettings(_factory); } /** * Return string representation. * @return string representation */ public String toString() { return _factory.toString(); } } /** * Nested class for adding a factory. */ private class AddFactoryChoice implements IFactoryChoice { /** * GUI used. */ private ShapesGUI _gui; /** * Make a new add factory choice. * @param gui Shapes GUI */ public AddFactoryChoice(ShapesGUI gui) { _gui = gui; } /** * Query for a string. * @param message message to display * @param prompt prompt to display * @param suggestion suggested value * @param parentComponent parent component * @return String object or null if error */ private String queryForString(String message, String prompt, String suggestion, Component parentComponent) { return (String)JOptionPane.showInputDialog(parentComponent, message, prompt, JOptionPane.QUESTION_MESSAGE, null, null, suggestion); } /** * Select this choice. */ public void select() { _gui._changeBtn.setEnabled(false); _gui.removeFactorySettings(); String className = queryForString("Add new shape","Shape class name:","",_gui._applet); _gui.addFactoryToChooser(className); } /** * Return string representation. * @return string representation */ public String toString() { return "Add ..."; } } }