package gui; import puzzledice.*; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.FileDialog; import java.awt.Rectangle; import java.awt.Toolkit; import javax.swing.JFrame; import javax.swing.BoxLayout; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.SwingConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; import java.awt.Component; import javax.swing.JTabbedPane; import javax.swing.JMenuBar; import javax.swing.JMenu; import java.awt.BorderLayout; import javax.swing.JMenuItem; import java.awt.event.ActionListener; import java.awt.event.ActionEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import javax.swing.KeyStroke; import java.awt.event.KeyEvent; import javax.swing.JSeparator; import com.mxgraph.layout.mxGraphLayout; import com.mxgraph.layout.mxOrganicLayout; import com.mxgraph.layout.hierarchical.mxHierarchicalLayout; import com.mxgraph.model.mxCell; import com.mxgraph.swing.mxGraphComponent; import com.mxgraph.swing.util.mxMorphing; import com.mxgraph.util.mxEventObject; import com.mxgraph.util.mxEvent; import com.mxgraph.util.mxEventSource.mxIEventListener; import com.mxgraph.view.mxGraph; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.io.BufferedWriter; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.nio.MappedByteBuffer; import java.nio.channels.FileChannel; import java.nio.charset.Charset; public class WindowMain { private static JFrame frmPuzzledicePuzzleEditor; //The layout component available for editing the graph private static mxOrganicLayout areaGraphLayout; private static mxGraph areaGraph; private static mxGraphComponent areaGraphComponent; private static mxOrganicLayout puzzleGraphOrganicLayout; private static mxHierarchicalLayout puzzleGraphHierLayout; private static mxGraph puzzleGraph; private static mxGraphComponent puzzleGraphComponent; private static mxCell hierarchyRoot; private AreaEditPanel areaEditPanel; private PuzzleEditPanel puzzleEditPanel; JTabbedPane graphPanel; private static JTextArea txtTextPanel; private File _openFile; private String _emptyXml; /** * Launch the application. */ public static void main(String[] args) { try { System.setProperty("apple.laf.useScreenMenuBar", "true"); System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Puzzle Map Editor"); UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) {} EventQueue.invokeLater(new Runnable() { @SuppressWarnings("static-access") public void run() { try { WindowMain window = new WindowMain(); window.frmPuzzledicePuzzleEditor.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); } /** * Create the application. */ public WindowMain() { initialize(); } /** * Initialize the contents of the frame. */ private void initialize() { frmPuzzledicePuzzleEditor = new JFrame(); frmPuzzledicePuzzleEditor.setTitle("Puzzledice Puzzle Map Editor"); frmPuzzledicePuzzleEditor.setBounds(100, 100, 743, 585); frmPuzzledicePuzzleEditor.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frmPuzzledicePuzzleEditor.getContentPane().setLayout(new BorderLayout(0, 0)); frmPuzzledicePuzzleEditor.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent winEvt) { if (onExit()) System.exit(0); } }); // Set up the program to catch OSX quit events try { OSXAdapter.setQuitHandler(this, this.getClass().getMethod("onExit", new Class[] {})); } catch (SecurityException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } JMenuBar menuBar = new JMenuBar(); frmPuzzledicePuzzleEditor.setJMenuBar(menuBar); JMenu mnFile = new JMenu("File"); menuBar.add(mnFile); JMenuItem mntmNew = new JMenuItem("New"); mntmNew.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); mntmNew.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { clear(); _openFile = null; } }); mnFile.add(mntmNew); JMenuItem mntmOpen = new JMenuItem("Open"); mntmOpen.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); mntmOpen.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { // Run the file dialogue later since it's blocking SwingUtilities.invokeLater(new Runnable() { public void run() { FileDialog chooser = new FileDialog(frmPuzzledicePuzzleEditor, "Open", FileDialog.LOAD); chooser.setVisible(true); if (chooser.getFile() != null) { clear(); File file = new File(chooser.getDirectory(), chooser.getFile()); if(Loader.LoadFromXML(file)) { areaEditPanel.justLoaded(); puzzleEditPanel.justLoaded(); _openFile = file; } else { JOptionPane.showMessageDialog(frmPuzzledicePuzzleEditor, "File failed to open!", "Error", JOptionPane.ERROR_MESSAGE); } } } }); } }); mnFile.add(mntmOpen); JMenuItem mntmSave = new JMenuItem("Save"); mntmSave.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); mntmSave.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { // Invoke the save later since it's a blocking method SwingUtilities.invokeLater(new Runnable() { public void run() { save(); } }); } }); mnFile.add(mntmSave); JMenuItem mntmSaveAs = new JMenuItem("Save As"); mntmSaveAs.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent evt) { SwingUtilities.invokeLater(new Runnable() { public void run() { String xml = xmlDigest(); saveAs(xml); } }); } }); mnFile.add(mntmSaveAs); JMenuItem mntmExit = new JMenuItem("Exit"); mntmExit.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, Toolkit.getDefaultToolkit().getMenuShortcutKeyMask())); mntmExit.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { if (onExit()) System.exit(0); } }); mnFile.add(mntmExit); JMenu mnHelp = new JMenu("Help"); menuBar.add(mnHelp); JPanel panel = new JPanel(); frmPuzzledicePuzzleEditor.getContentPane().add(panel); panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); JPanel editPanel = new JPanel(); panel.add(editPanel); editPanel.setLayout(new BoxLayout(editPanel, BoxLayout.X_AXIS)); JSeparator separator_2 = new JSeparator(); separator_2.setMinimumSize(new Dimension(5, 0)); separator_2.setMaximumSize(new Dimension(5, 0)); separator_2.setPreferredSize(new Dimension(5, 0)); editPanel.add(separator_2); areaEditPanel = new AreaEditPanel(); editPanel.add(areaEditPanel); JSeparator separator = new JSeparator(); separator.setMinimumSize(new Dimension(5, 0)); separator.setMaximumSize(new Dimension(5, 0)); separator.setPreferredSize(new Dimension(5, 0)); editPanel.add(separator); puzzleEditPanel = new PuzzleEditPanel(); editPanel.add(puzzleEditPanel); JSeparator separator_1 = new JSeparator(); separator_1.setMinimumSize(new Dimension(5, 0)); separator_1.setMaximumSize(new Dimension(5, 0)); separator_1.setPreferredSize(new Dimension(5, 0)); editPanel.add(separator_1); graphPanel = new JTabbedPane(JTabbedPane.TOP); graphPanel.setPreferredSize(new Dimension(frmPuzzledicePuzzleEditor.getBounds().width, frmPuzzledicePuzzleEditor.getBounds().height)); panel.add(graphPanel); graphPanel.setAlignmentY(Component.TOP_ALIGNMENT); areaGraph = new mxGraph(); areaGraph.setAutoSizeCells(true); areaGraphComponent = new mxGraphComponent(areaGraph); areaGraphComponent.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent arg0) { areaGraphLayout = new mxOrganicLayout(areaGraph, new Rectangle(0, 0, graphPanel.getBounds().width, graphPanel.getBounds().height)); areaGraphLayout.execute(areaGraph.getDefaultParent()); } }); areaGraphComponent.setEnabled(false); areaGraphLayout = new mxOrganicLayout(areaGraph, new Rectangle(0, 0, graphPanel.getBounds().width, graphPanel.getBounds().height)); areaGraph.setEnabled(false); graphPanel.addTab("Area Graph", null, areaGraphComponent, null); puzzleGraph = new mxGraph(); puzzleGraph.setAutoSizeCells(true); puzzleGraphComponent = new mxGraphComponent(puzzleGraph); puzzleGraphComponent.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent arg0) { } }); puzzleGraphComponent.setEnabled(false); puzzleGraphOrganicLayout = new mxOrganicLayout(puzzleGraph, new Rectangle(0, 0, graphPanel.getBounds().width, graphPanel.getBounds().height)); puzzleGraphHierLayout = new mxHierarchicalLayout(puzzleGraph, SwingConstants.WEST); graphPanel.addTab("Puzzle Graph", null, puzzleGraphComponent, null); txtTextPanel = new JTextArea("Textual description"); panel.add(txtTextPanel); txtTextPanel.setLineWrap(true); txtTextPanel.setWrapStyleWord(true); txtTextPanel.setEditable(false); frmPuzzledicePuzzleEditor.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent arg0) { graphPanel.setPreferredSize(new Dimension(frmPuzzledicePuzzleEditor.getBounds().width, frmPuzzledicePuzzleEditor.getBounds().height)); } }); _emptyXml = xmlDigest(); } // function that executes the area graph layout through an animation. public static void updateAreaGraph() { frmPuzzledicePuzzleEditor.setEnabled(false); areaGraph.getModel().beginUpdate(); try { areaGraphLayout.execute(areaGraph.getDefaultParent()); } finally { mxMorphing morph = new mxMorphing(areaGraphComponent, 20, 1.2, 20); morph.addListener(mxEvent.DONE, new mxIEventListener() { public void invoke(Object sender, mxEventObject evt) { frmPuzzledicePuzzleEditor.setEnabled(true); areaGraph.getModel().endUpdate(); } }); morph.startAnimation(); } } // Create a new document public void clear() { areaEditPanel.clear(); puzzleEditPanel.clear(); areaGraph = new mxGraph(); areaGraph.setAutoSizeCells(true); areaGraphComponent.setGraph(areaGraph); areaGraphLayout = new mxOrganicLayout(areaGraph, new Rectangle(0, 0, graphPanel.getBounds().width, graphPanel.getBounds().height)); areaGraph.setEnabled(false); puzzleGraph = new mxGraph(); puzzleGraph.setAutoSizeCells(true); puzzleGraphComponent.setGraph(puzzleGraph); puzzleGraphOrganicLayout = new mxOrganicLayout(puzzleGraph, new Rectangle(0, 0, graphPanel.getBounds().width, graphPanel.getBounds().height)); puzzleGraphHierLayout = new mxHierarchicalLayout(puzzleGraph, SwingConstants.WEST); hierarchyRoot = null; AreaEditPanel.reset(); CombinePuzzleBlock.reset(); InsertionPuzzleBlock.reset(); DoorUnlockBlock.reset(); FilterBlock.reset(); InsertionPuzzleBlock.reset(); ItemRequestPuzzleBlock.reset(); PropertyChangePuzzleBlock.reset(); SpawnPuzzleBlock.reset(); OutputBlock.reset(); ORBlock.reset(); } // function that executes the puzzle graph layout(s) through an animation // Uses the organic layout to first separate independent nodes, then uses the // hierarchical layout to make it look nifty public static void updatePuzzleGraph() { frmPuzzledicePuzzleEditor.setEnabled(false); puzzleGraph.getModel().beginUpdate(); try { puzzleGraphHierLayout.execute(puzzleGraph.getDefaultParent()); } finally { mxMorphing morph = new mxMorphing(puzzleGraphComponent, 20, 1.2, 20); morph.addListener(mxEvent.DONE, new mxIEventListener() { public void invoke(Object sender, mxEventObject evt) { puzzleGraph.getModel().endUpdate(); frmPuzzledicePuzzleEditor.setEnabled(true); } }); morph.startAnimation(); } } // Public function that allows the editing panels access to the graph layout public static mxGraphLayout getAreaGraphLayout() { return areaGraphLayout; } public static mxGraph getAreaGraph() { return areaGraph; } public static mxGraph getPuzzleGraph() { return puzzleGraph; } public static mxCell getHierarchyRoot() { if(hierarchyRoot == null) { hierarchyRoot = (mxCell)puzzleGraph.insertVertex(puzzleGraph.getDefaultParent(), null, "Start", 0, 0, 0, 0); puzzleGraph.updateCellSize(hierarchyRoot); } return hierarchyRoot; } public static mxGraphLayout getPuzzleGraphOrganicLayout() { return puzzleGraphOrganicLayout; } public static void updateTextDescription(String newDescription) { txtTextPanel.setText(newDescription); } private boolean save() { String xml = xmlDigest(); if (_openFile != null) { try { BufferedWriter writer = new BufferedWriter(new FileWriter(_openFile)); writer.write(xml); writer.close(); return true; } catch (IOException ioe) { ioe.printStackTrace(); return false; } } else return saveAs(xml); } private boolean saveAs(String xml) { FileDialog chooser = new FileDialog(frmPuzzledicePuzzleEditor, "Save", FileDialog.SAVE); chooser.setVisible(true); if (chooser.getFile() != null) { try { File saveFile = new File(chooser.getDirectory(), chooser.getFile()); BufferedWriter writer = new BufferedWriter(new FileWriter(saveFile)); writer.write(xml); writer.close(); _openFile = saveFile; return true; } catch (IOException ioe) { ioe.printStackTrace(); return false; } } return false; } public static String xmlDigest() { // Go through our whole list of areas and puzzles and print their xml String xml = "<puzzleMap>\n"; xml += "<areas>\n"; AreaBlock[] areas = AreaEditPanel.getAreaList(); for (AreaBlock area : areas) { xml += area.toXML() + "\n"; } xml += "</areas>\n<puzzles>\n"; PuzzleBlock[] puzzles = PuzzleEditPanel.getBlockList(); for (PuzzleBlock puzzle : puzzles) { xml += puzzle.toXML() + "\n"; } xml += "</puzzles>\n</puzzleMap>"; return xml; } public boolean onExit() { if (!unsavedChanges()) return true; else { int response = JOptionPane.showConfirmDialog(frmPuzzledicePuzzleEditor, "Save Unsaved Changes?", "Save?", JOptionPane.YES_NO_CANCEL_OPTION); if ((response == JOptionPane.YES_OPTION && save()) || response == JOptionPane.NO_OPTION) return true; } return false; } private Boolean unsavedChanges() { String currentXml = xmlDigest(); String oldXml; if (_openFile == null) oldXml = _emptyXml; else { try { FileInputStream stream = new FileInputStream(_openFile); FileChannel fc = stream.getChannel(); MappedByteBuffer bb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size()); /* Instead of using default, pass in a decoder. */ oldXml = Charset.defaultCharset().decode(bb).toString(); } catch (Exception e) { e.printStackTrace(); return true; } } return !(currentXml.trim()).equals(oldXml.trim()); } }