import java.awt.BorderLayout; import java.awt.GridLayout; import java.awt.Image; import java.awt.event.*; import javax.swing.*; /** * This is a view for a sliding n-puzzle with one image * serving as the soruce for many different pieces each * of which can slide. Clients can also specify a null * image which results in simple, numbered, and colored squares * being used for each piece of the n-puzzle. *
* This is the view, it talks to a controller which mediates between * a view (or several) and the view(s) model. * * @author Owen Astrachan */ public class PuzzleGui extends JFrame implements PuzzleView { private PuzzleController myControl; private JTextField myText; private ButtonPanel myButtonPanel; private JMenuItem myUndo; private static final int OUR_WIDTH = 500; private static final int OUR_HEIGHT = 500; public PuzzleGui(PuzzleController control) { this(control,null); } /** * Create a gui with a model and an image (image optional) * @param imageSource is null or the url/file for an image * @param model is the model for this view */ public PuzzleGui(PuzzleController control, String imageSource) { setTitle("OOGA Puzzle"); JPanel panel = new JPanel(new BorderLayout()); myControl = control; myControl.addView(this); myText = new JTextField(20); myButtonPanel = new ButtonPanel(myControl.getModelSize(), imageSource); panel.add(myButtonPanel, BorderLayout.CENTER); panel.add(myText, BorderLayout.SOUTH); setContentPane(panel); setDefaultCloseOperation(EXIT_ON_CLOSE); makeMenu(); constrainResize(); pack(); setSize(OUR_WIDTH, OUR_HEIGHT); setVisible(true); } /** * Required by interface, displayes text message. * @param text is to be displayed by view */ public void showText(String text) { myText.setText(text); } /** * Required by interface, enables/disables undo invoker * @param value sets the status of the undo (button, menu-item, etc.) */ public void setEnabledUndo(boolean value) { myUndo.setEnabled(value); } /** * Required by interface, show the grid that's the model * @param list represents the model */ public void showGrid(int[] list) { myButtonPanel.setGrid(list); } /** * Make resizing result in a square view, so "snap" * to squareness before resizing takes effect. */ private void constrainResize() { addComponentListener(new ComponentAdapter(){ public void componentResized(ComponentEvent e) { int w = getWidth(); int h = getHeight(); w = Math.max(w,h); setSize(w,w); } }); } private void makeMenu() { JMenu menu = new JMenu("Puzzle"); myUndo = new JMenuItem(new AbstractAction("Undo") { public void actionPerformed(ActionEvent e) { myControl.undoMove(); } }); setEnabledUndo(false); menu.add(myUndo); menu.add(new AbstractAction("Quit") { public void actionPerformed(ActionEvent e) { System.exit(0); } }); JMenuBar menubar = new JMenuBar(); menubar.add(menu); setJMenuBar(menubar); } public static void main(String args[]) { PuzzleController control = new PuzzleController(); PuzzleModel model = new PuzzleModel(control,10); PuzzleGui pg = new PuzzleGui(control); } class ButtonPanel extends JPanel { private JButton myButtons[]; /** * Create a button panel consisting of nButtons per side * of a square (total # buttons is nButtons*nButtons) using * the designated imageSource (if not null). * @param nButtons is the number of buttons PER SIDE * @param imageSource is the source of the image for this panel */ ButtonPanel(int nButtons, String imageSource) { // construct superclass and make button array super(new GridLayout(nButtons,nButtons)); myButtons = new JButton[nButtons*nButtons]; // load the image if one is specified Image image = null; if (imageSource != null) { image = ImageFactory.getImage(this, imageSource); } // make listeners for buttons in panel // first one to show text in this GUI ActionListener textDisplayer = new ActionListener(){ public void actionPerformed(ActionEvent e) { showText(e.getActionCommand()); } }; // make listener to do the move chosen by user ActionListener moveMaker = new ActionListener(){ public void actionPerformed(ActionEvent e) { int val = Integer.parseInt(e.getActionCommand()); myControl.makeMove(new PuzzleMove(val)); } }; makeButtons(image,textDisplayer,moveMaker); } /** * Makes the buttons for this GUI/view. The number * of buttons is determined my the size of the array * myButtons. This helper function takes some of * the busy work out of the ButtonPanel constructor. */ private void makeButtons(Image image, ActionListener textDisplayer, ActionListener moveMaker) { for(int k=0; k < myButtons.length; k++) { String label = ""+k; String iLabel = label; if (k == myButtons.length-1) { iLabel = PuzzleConsts.BLANK; } Icon icon; if (image == null) { icon = new PlainPuzzleIcon(iLabel, myControl.getModelSize()); } else { icon = new ImagePuzzleIcon(image, iLabel, myControl.getModelSize()); } // create button with icon myButtons[k] = new JButton(icon); myButtons[k].setActionCommand(label); myButtons[k].addActionListener(textDisplayer); myButtons[k].addActionListener(moveMaker); add(myButtons[k]); } } /** * Set all the buttons by re-displaying them all * in the right order. First we remove all the components * in this panel (that's the buttons). Then we add the * buttons to this panel in the right order based on what * the ordering of the buttons in the model is. * Finally, we revalidate so the buttons are shown (GUI * will redraw as a result of the revalidate). */ public void setGrid(int list[]) { removeAll(); for(int k=0; k < list.length; k++) { add(myButtons[list[k]]); } revalidate(); } } }