package org.artisanlogiciel.games.maze.gui; import java.awt.BorderLayout; import java.awt.image.BufferedImage; import java.awt.Container; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.*; import java.util.*; import javax.imageio.ImageIO; import javax.swing.*; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.artisanlogiciel.games.maze.*; import org.artisanlogiciel.games.maze.persist.MazePersistRaw; import org.artisanlogiciel.games.maze.solve.SolvingModel; import org.artisanlogiciel.games.stl.Maze3dParams; import org.artisanlogiciel.games.stl.Wall3dStream; import org.artisanlogiciel.osm.OsmReader; import org.artisanlogiciel.osm.convert.OsmToDrawing; import org.artisanlogiciel.util.UTF8Control; import org.artisanlogiciel.graphics.Drawing; import org.artisanlogiciel.graphics.DrawingLine; import org.artisanlogiciel.graphics.SvgWriter; /** * Display is Main JFrame for this tool **/ public class Display extends JFrame implements StatusListener { // to please eclipse, not supposed to be serialized private static final long serialVersionUID = 8500214871372184418L; public final static ResourceBundle labels = ResourceBundle.getBundle("LabelsBundle", Locale.getDefault(), Display.class.getClassLoader(), new UTF8Control()); MazeComponent maze; MazeControler controler; LabyModel model; boolean autoSize; boolean statusEnable = true; MazeParams params = null; JTextField statusField = null; Maze3dSettings stlsettings; Display(LabyModel model, int W, int H, MazeParams params) { super(labels.getString("title")); if (params != null) { // fixedParams = new MazeParamsFixed(params.getSaveDir(),params.getWidth(),params.getHeight(),params.getMaxDepth()); this.params = params; } this.model = model; maze = createMazeComponent(model, W, H); Container con = this.getContentPane(); JScrollPane scrollableMaze = new JScrollPane(maze); scrollableMaze.addMouseMotionListener(maze); con.add(scrollableMaze, BorderLayout.CENTER); controler = new MazeControler(params); con.add(controler.getMoveControl(), BorderLayout.NORTH); con.add(controler.getGenerationControl(), BorderLayout.SOUTH); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (autoSize) { maze.getAutoSize(); } } }); model.setMazeListener(maze); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(W, H, W, H); setVisible(true); } private MazeComponent createMazeComponent(LabyModel model, int W, int H) { MazeCellParameters cp = new MazeCellParameters(model.getWidth(), model.getHeight(), W, H, 3, 3); MazeComponent comp = new MazeComponent(model, cp, this); return comp; } void recreateModel() { // recreate labyrinth if (params != null) { // Keep current model to be able to complete a manualy edited one maze.resetWallsProvider(model); model.setMazeListener(maze); model.reset(); // we are in GUI event thread, need to release it and do it outside. new Thread() { public void run() { model.generateWithEntry(0, 0); model.addEntryOrExit(-1, 0); model.addEntryOrExit(params.getWidth(), params.getHeight() - 1); } }.start(); } } void resetModel() { // recreate labyrinth if (params != null) { params = controler.getSettings().resetParams(); model = new LabyModel(params); refresh(); } } void refresh() { // reinit labyrinth view if (params != null) { maze.resetWallsProvider(model); model.setMazeListener(maze); // we are in GUI event thread, need to release it and do it outside. new Thread() { public void run() { maze.changed(null, null, model); } }.start(); } } void resolve() { SolvingModel solving = new SolvingModel(model); // should transform current model to be a SolvingModel model = solving; model.reset(); solving.resolve(solving.getWidth() - 1, solving.getHeight() - 1, maze); } void goNorth() { maze.goNorth(); } void goSouth() { maze.goSouth(); } void goEast() { maze.goEast(); } void goWest() { maze.goWest(); } void setWallSize(int size) { maze.setWallSize(size); } void setAutoSize(boolean autoSize) { this.autoSize = autoSize; if (autoSize) { maze.getAutoSize(); } } void addWallInDrawing(int pX, int pY, Drawing d) { short walls = model.getWalls(pX, pY); short wdrawn = 0; // todo int w = 2; int h = 2; int ox = 0; int oy = 0; int x = ox + (int) (pX * w); int y = oy + (int) (pY * h); DrawingLine dl = new DrawingLine(); // order matters since all points are linked if ((pY == 0) && LabyModel.isFlagSet(walls, Brick.UP)) { dl.addPoint(new Point(x, y)); dl.addPoint(new Point(x + (int) w, y)); wdrawn |= Brick.UP; } if (LabyModel.isFlagSet(walls, Brick.RIGHT)) { if (!LabyModel.isFlagSet(wdrawn, Brick.UP)) { dl.addPoint(new Point(x + (int) w, y)); } dl.addPoint(new Point(x + (int) w, y + (int) h)); wdrawn |= Brick.RIGHT; } if (LabyModel.isFlagSet(walls, Brick.DOWN)) { if (!LabyModel.isFlagSet(wdrawn, Brick.RIGHT)) { if (wdrawn != 0) { d.addLine(dl); dl = new DrawingLine(); } dl.addPoint(new Point(x + (int) w, y + (int) h)); } dl.addPoint(new Point(x, y + (int) h)); wdrawn |= Brick.DOWN; } if ((pX == 0) && LabyModel.isFlagSet(walls, Brick.LEFT)) { if (!LabyModel.isFlagSet(wdrawn, Brick.DOWN)) { if (wdrawn != 0) { d.addLine(dl); dl = new DrawingLine(); } dl.addPoint(new Point(x, y + (int) h)); } dl.addPoint(new Point(x, y)); wdrawn |= Brick.LEFT; } if (wdrawn != 0) { d.addLine(dl); } } Drawing createDrawing() { Drawing d = new Drawing(); { short walls; // draw all walls within clip bounds horiz first then lines for (int y = 0; y < model.getHeight(); y++) { for (int x = 0; x < model.getWidth(); x++) { addWallInDrawing(x, y, d); } } } return d; } // to allow to log / write somewher into screen... void writeSentence(String pSentence) { // TODO addStatus(pSentence); } void writeError(String pError) { System.err.println(pError); } void saveImc() { Drawing d = createDrawing(); if (d != null) { File outfile = getFileForExtension("imc"); writeSentence("Saving to " + outfile + " ..."); try { DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile)); d.saveLinesKompressed(out); out.flush(); out.close(); writeSentence("... Done."); } catch (IOException io) { io.printStackTrace(System.err); } } } File getFileForExtension(final String extension) { return new File(params.getSaveDir(), params.getName() + "." + extension); } void saveSvg() { Drawing d = createDrawing(); if (d != null) { File outfile = getFileForExtension("svg"); writeSentence("Saving to " + outfile + " ..."); try { DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile)); SvgWriter writer = new SvgWriter(d.getInternLines()); writer.writeTo(out); out.flush(); out.close(); writeSentence("... Done."); } catch (IOException io) { io.printStackTrace(System.err); } } else { writeError("drawing creation failed"); } } void savePng() { File file = getFileForExtension("png"); // BufferedImage bi = new BufferedImage(this.getSize().width, this.getSize().height, BufferedImage.TYPE_INT_ARGB); BufferedImage bi = new BufferedImage(maze.getSize().width, maze.getSize().height, BufferedImage.TYPE_INT_ARGB); Graphics g = bi.createGraphics(); // this.paint(g); maze.paint(g); g.dispose(); try { ImageIO.write(bi, "png", file); } catch (Exception e) { e.printStackTrace(); } } void saveText() { File outfile = getFileForExtension("txt"); writeSentence("Saving to " + outfile + " ..."); try { DataOutputStream out = new DataOutputStream(new FileOutputStream(outfile)); out.write(model.toLabyMap().toString().getBytes()); out.flush(); out.close(); writeSentence("... Done."); } catch (IOException io) { io.printStackTrace(System.err); } } public void addStatus(String pStatus) { if ( statusEnable ) { statusField.setText(pStatus); } } private class MazeControler extends JPanel { /** * */ private static final long serialVersionUID = 1L; private MazeSettings settings = null; private JPanel resizecontrol = null; private void setMazeName(String pName) { MazeParamsFixed p = (MazeParamsFixed) params; p.setName(pName); } private JMenu createLoadingMenu() { JMenu loadMenu = new JMenu(labels.getString("load") ); final JTextField loadName = new JTextField("newlaby"); loadMenu.add(loadName); Action loadRawAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("load raw"); String filename = loadName.getText(); if ((filename.length() > 0)) { setMazeName(filename); loadRaw(); refresh(); } } }; JButton loadRawButton = new JButton(labels.getString("load" ) + " raw"); loadRawButton.addActionListener(loadRawAction); loadMenu.add(loadRawButton); Action loadImcAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("load Imc"); String filename = loadName.getText(); if ((filename.length() > 0)) { setMazeName(filename); loadImc(false); refresh(); } } }; JButton loadImcButton = new JButton(labels.getString("load" ) + " imc"); loadImcButton.addActionListener(loadImcAction); loadMenu.add(loadImcButton); Action loadDrawingAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("load Drawing"); String filename = loadName.getText(); if ((filename.length() > 0)) { setMazeName(filename); loadDrawing(false); refresh(); } } }; JButton loadDrawingButton = new JButton(labels.getString("load" ) + " Drawing"); loadDrawingButton.addActionListener(loadDrawingAction); loadMenu.add(loadDrawingButton); Action loadOsmAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("load Osm"); String filename = loadName.getText(); if ((filename.length() > 0)) { setMazeName(filename); loadOsm(false); refresh(); } } }; JButton loadOsmButton = new JButton(labels.getString("load" ) + " Osm"); loadOsmButton.addActionListener(loadOsmAction); loadMenu.add(loadOsmButton); return loadMenu; } private JMenu createSavingMenu() { final JTextField saveName = new JTextField("newlaby"); final JButton savePngButton = new JButton(labels.getString("save") + " png"); Action savePngAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("save png"); setMazeName(saveName.getText()); savePng(); } }; savePngButton.addActionListener(savePngAction); final JButton saveSvgButton = new JButton(labels.getString("save") + " svg"); Action saveSvgAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { writeSentence("save svg"); setMazeName(saveName.getText()); saveSvg(); } }; saveSvgButton.addActionListener(saveSvgAction); final JButton saveButton = new JButton(labels.getString("save") + " raw"); Action saveAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("save"); setMazeName(saveName.getText()); MazeParamsFixed p = (MazeParamsFixed) params; save(p, model); } }; saveButton.addActionListener(saveAction); final JButton saveImcButton = new JButton(labels.getString("save") + " imc"); Action saveImcAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("save imc"); setMazeName(saveName.getText()); saveImc(); } }; saveImcButton.addActionListener(saveImcAction); final JButton saveStlButton = new JButton(labels.getString("save") + " stl"); Action saveStlAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("save stl"); setMazeName(saveName.getText()); MazeParamsFixed p = (MazeParamsFixed) params; saveStl(p, model,stlsettings.createParams()); } }; saveStlButton.addActionListener(saveStlAction); final JButton saveTextButton = new JButton(labels.getString("save") + " txt"); Action saveTextAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("save txt"); setMazeName(saveName.getText()); saveText(); } }; saveTextButton.addActionListener(saveTextAction); stlsettings = new Maze3dSettings(new Maze3dParams()); JMenu saveMenu = new JMenu(labels.getString("save") ); saveMenu.add(saveName); saveMenu.add(saveSvgButton); saveMenu.add(savePngButton); saveMenu.add(saveButton); saveMenu.add(stlsettings); saveMenu.add(saveStlButton); saveMenu.add(saveImcButton); saveMenu.add(saveTextButton); return saveMenu; } private JPanel createResolveQuitBar() { JPanel resolveQuitBar = new JPanel(new FlowLayout()); JButton buttonCreate = new JButton(labels.getString("create")); buttonCreate.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { recreateModel(); } }); resolveQuitBar.add(buttonCreate); JButton resolveButton = new JButton(labels.getString("resolve")); resolveButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { // addStatus("Resolving"); resolve(); } }); resolveQuitBar.add(resolveButton); final JButton quitButton = new JButton(labels.getString("quit")); Action quitAction = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("quit"); System.exit(0); } }; quitButton.addActionListener(quitAction); resolveQuitBar.add(quitButton); JButton buttonReset = new JButton("reset");//labels.getString("reset")); buttonReset.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { resetModel(); } }); resolveQuitBar.add(buttonReset); return resolveQuitBar; } public MazeControler(MazeParams params) { // super(new BorderLayout()); super(); BoxLayout layout = new BoxLayout(this, BoxLayout.Y_AXIS); setLayout(layout); JMenuBar menuBar = new JMenuBar(); menuBar.add(createSavingMenu()); menuBar.add(createLoadingMenu()); add(menuBar); add(createResolveQuitBar()); JPanel controlMovesPanel = new JPanel(new BorderLayout()); settings = new MazeSettings(params); @SuppressWarnings("serial") Action goNorth = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("go North"); goNorth(); } }; JButton north = addDirection(this, labels.getString("north"), "UP", goNorth); @SuppressWarnings("serial") Action goEast = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("go East"); goEast(); } }; JButton east = addDirection(this, labels.getString("east"), "RIGHT", goEast); @SuppressWarnings("serial") Action goWest = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("go West"); goWest(); } }; JButton west = addDirection(this, labels.getString("west"), "LEFT", goWest); @SuppressWarnings("serial") Action goSouth = new AbstractAction() { public void actionPerformed(ActionEvent evt) { // addStatus("go South"); goSouth(); } }; JButton south = addDirection(this, labels.getString("south"), "DOWN", goSouth); controlMovesPanel.add(north, BorderLayout.NORTH); controlMovesPanel.add(west, BorderLayout.WEST); controlMovesPanel.add(east, BorderLayout.EAST); controlMovesPanel.add(south, BorderLayout.SOUTH); JPanel controlPanel = new JPanel(); controlPanel.add(controlMovesPanel,BorderLayout.LINE_START); statusField = new JTextField("",20); controlPanel.add(statusField, BorderLayout.CENTER); add(controlPanel, BorderLayout.NORTH); // control display panel contains controls for display // zoom , autozoom. final JPanel controlDisplayPanel = new JPanel(new BorderLayout()); final JCheckBox showAll = new JCheckBox("show all"); showAll.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { maze.setShowAll(showAll.isSelected()); } }); final JSlider slider = new JSlider(2, 40); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { setWallSize(slider.getValue()); } }); final JCheckBox autoSlide = new JCheckBox("autoslide"); autoSlide.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { setAutoSize(autoSlide.isSelected()); } }); resizecontrol = new JPanel(new FlowLayout()); resizecontrol.add(showAll); resizecontrol.add(slider); resizecontrol.add(autoSlide); add(controlDisplayPanel, BorderLayout.SOUTH); add(resizecontrol, BorderLayout.WEST); add(settings, BorderLayout.EAST); } private JPanel getMoveControl() { return this; } private JPanel getResizeControl() { return resizecontrol; } private JPanel getGenerationControl() { return settings; } public MazeSettings getSettings() { return settings; } } private void loadRaw() { File infile = new File(params.getSaveDir(), params.getName() + ".raw"); FileInputStream inputStream = null; try { inputStream = new FileInputStream(infile); model = new MazePersistRaw().parseInputStream("raw",inputStream); } catch (IOException io) { io.printStackTrace(System.err); statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath()); } finally { if (inputStream != null ) { // cleanup try { inputStream.close(); } catch (Exception any) { // don't care really } } } } private void loadOsm(boolean add) { if ( maze != null ) { new Thread() { @Override public void run() { File infile = new File(params.getSaveDir(), params.getName() + ".osm"); FileInputStream inputStream = null; try { // TODO really use InputStream and not pathname OsmReader reader = new OsmReader(infile.getCanonicalPath()); reader.read(); OsmToDrawing converter = new OsmToDrawing(reader, 25000,25000); Drawing drawing = converter.getDrawing(reader.getWays()); statusEnable = false; maze.addDrawing(drawing,add); statusEnable = true; } catch (IOException io) { io.printStackTrace(System.err); statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath()); } finally { if (inputStream != null) { // cleanup try { inputStream.close(); } catch (Exception any) { // don't care really } } } } }.start(); } } private void loadImc(boolean add) { if ( maze != null ) { new Thread() { @Override public void run() { File infile = new File(params.getSaveDir(), params.getName() + ".imc"); FileInputStream inputStream = null; try { inputStream = new FileInputStream(infile); // TODO // model = new MazePersistRaw().parseInputStream("raw",inputStream); Drawing drawing = new Drawing(); drawing.loadLinesExpanded(new DataInputStream(inputStream)); statusEnable = false; maze.addDrawing(drawing,add); statusEnable = true; } catch (IOException io) { io.printStackTrace(System.err); statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath()); } finally { if (inputStream != null) { // cleanup try { inputStream.close(); } catch (Exception any) { // don't care really } } } } }.start(); } } private void loadDrawing(boolean add) { if ( maze != null ) { new Thread() { @Override public void run() { File infile = new File(params.getSaveDir(), params.getName() + ".drawing"); FileInputStream inputStream = null; try { inputStream = new FileInputStream(infile); // TODO // model = new MazePersistRaw().parseInputStream("raw",inputStream); Drawing drawing = new Drawing(); drawing.loadLines(new DataInputStream(inputStream)); statusEnable = false; maze.addDrawing(drawing,add); statusEnable = true; } catch (IOException io) { io.printStackTrace(System.err); statusField.setText("[ERROR] Can't load " + infile.getAbsolutePath()); } finally { if (inputStream != null) { // cleanup try { inputStream.close(); } catch (Exception any) { // don't care really } } } } }.start(); } } private JButton addDirection(final JPanel panel, final String direction, String key, Action goAction) { String actionName = "go" + direction; JButton button = new JButton(direction); button.addActionListener(goAction); KeyStroke keystroke = KeyStroke.getKeyStroke(key); // attach keys to maze maze.getInputMap().put(keystroke, actionName); maze.getActionMap().put(actionName, goAction); return button; } private static void setupDisplay(LabyModel model, int W, int H, MazeParams params) { Display display = new Display(model, W, H, params); } public void saveStl(MazeParamsFixed params, LabyModel model, Maze3dParams wallparams) { File outfile = getFileForExtension("stl"); if (!outfile.exists()) { addStatus("Saving to " + outfile + " ..."); try { FileOutputStream out = new FileOutputStream(outfile); new Wall3dStream(params.getName(), model, out, wallparams).stream(); out.close(); addStatus("... Done."); } catch (IOException io) { io.printStackTrace(System.err); } } else { addStatus("" + outfile + " already exists"); } } public void save(MazeParamsFixed params, LabyModel model) { File outfile = getFileForExtension("raw"); if (!outfile.exists()) { addStatus("Saving to " + outfile + " ..."); try { FileOutputStream out = new FileOutputStream(outfile); MazePersistRaw persist = new MazePersistRaw(model); persist.streamOut("raw", out); out.flush(); out.close(); addStatus("... Done."); } catch (IOException io) { io.printStackTrace(System.err); } } else { addStatus("" + outfile + " already exists"); } } public static void main(String pArgs[]) { LabyModel model = null; int W = 600; int H = 400; System.out.println("Default Locale " + Locale.getDefault()); if ((pArgs.length > 0) && (pArgs[0].length() > 0)) { try { model = new MazePersistRaw().parseInputStream("raw",new FileInputStream(pArgs[0])); } catch (IOException io) { io.printStackTrace(System.err); System.exit(1); } setupDisplay(model, W, H, null); } else { MazeParamsFixed params = new MazeParamsFixed(new File("lab"), 20, 20, 12,1024L); model = new LabyModel(params); setupDisplay(model, W, H, params); /* model.generateWithEntry(0, 0); model.addEntryOrExit(-1, 0); model.addEntryOrExit(params.getWidth(), params.getHeight() - 1); addStatus("Generation completed"); */ /* */ } } }