package genegui; import java.awt.*; import javax.swing.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.beans.PropertyVetoException; import java.beans.XMLDecoder; import java.beans.XMLEncoder; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.net.MalformedURLException; import java.net.URL; import java.util.Enumeration; import java.util.Vector; import main.AffectorTemplate; import main.GeneNet; import main.Model; import main.NodeTemplate; /** MainGui.java @author WJS */ public class MainGui extends JFrame implements GuiInterface, ModelStateChangeListener { private static final String URLIntro = "file:///"; /** The starting point for help files */ private static final String DefaultHelpURLString = URLIntro+System.getProperty("user.dir")+"/doc/hello.html"; /** Edit menu bar*/ private JMenu EditMenu; /** File menu bar*/ private JMenu FileMenu; /** A List of GUIListeners */ private Vector GUIListeners = new Vector(); /** Help menu bar*/ private JMenu HelpMenu; /** Last time display value used to squelch excess painting on Macs */ private float LastTime = 0f; /** Main menubar*/ private JMenuBar MainMenu; /** The desktop pane for holding all frames */ private JDesktopPane MainPanel; /** The file handle for the model/network loaded through this GUI */ private File ModelFile; /** The OSX console process */ private Process OSXConsole; /** Toolbar button for resting the model */ private JToggleButton ResetButton; /** Toolbar button for starting single model runs */ private JToggleButton RunButton; /** The run menubar */ private JMenu RunMenu; /** Self reference */ private MainGui SaveThis = this; /** Toolbar button for stepping through the model */ private JToggleButton StepButton; /** Console object that standard output is re-directed to */ private Console TheConsole; /** Console object that errors are re-directed to */ private Console TheErrorConsole; /** The User COnfiguration object */ private Configuration TheConfiguration; /** The GeneNet object that this GUI communicates with */ private GeneNet TheGeneNet; /** The unique help window */ private HTMLViewer TheHelpWindow; /** A copy of the model that must be updated whenever a new network is loaded */ private Model TheModel; /** Field for displaying time step and other information */ private JLabel TimeField; /** Time label is also a button for disabling the time display */ private JToggleButton TimeLabelButton; /** Holds MainPanel and JToolBar */ private JPanel TopPanel; /** The viewer menu */ private JMenu ViewMenu; /** Simple constructor */ public MainGui(GeneNet theGeneNet) { super("Ingeneue"); TheGeneNet = theGeneNet; // Build the display and all menus TopPanel = new JPanel(); TopPanel.setLayout(new BorderLayout()); MainPanel = new JDesktopPane(); MainPanel.setBackground(Color.lightGray); TopPanel.add(MainPanel,BorderLayout.CENTER); setContentPane(TopPanel); // Setup a Jmenu MainMenu = new JMenuBar(); MainMenu.setBackground(Color.lightGray); JMenuItem item; KeyStroke ks; // Make the FileMenu FileMenu = new JMenu("File"); FileMenu.setBackground(Color.lightGray); item = new JMenuItem("New"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { TheGeneNet.runControl(GuiInterface.STOP_ITERATOR); TheGeneNet.networkIO(GuiInterface.NEW,null); clearGUI(); } }); FileMenu.add(item); item = new JMenuItem("Open"); ks = KeyStroke.getKeyStroke('O', java.awt.Event.CTRL_MASK); item.setAccelerator(ks); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { FileDialog chooser = new FileDialog(SaveThis,"Choose File"); chooser.setDirectory(TheConfiguration.getLastOpenedDir()); chooser.show(); if (chooser.getFile()!=null) { openAFile(chooser.getDirectory(),chooser.getFile()); } } }); FileMenu.add(item); item = new JMenuItem("Close"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { TheGeneNet.runControl(GuiInterface.STOP_ITERATOR); TheGeneNet.networkIO(GuiInterface.CLOSE,null); clearGUI(); TheConfiguration.setModelFile(null); TheConfiguration.setIteratorFile(null); } }); FileMenu.add(item); FileMenu.addSeparator(); item = new JMenuItem("Save"); ks = KeyStroke.getKeyStroke('S', java.awt.Event.CTRL_MASK); item.setAccelerator(ks); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { TheGeneNet.networkIO(GuiInterface.SAVE,ModelFile); } }); FileMenu.add(item); item = new JMenuItem("Save As ..."); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { FileDialog chooser = new FileDialog(SaveThis,"Choose File"); chooser.setDirectory(TheConfiguration.getLastSavedDir()); chooser.show(); if (chooser.getFile()!=null) { File file = new File(chooser.getDirectory()+chooser.getFile()); if (TheGeneNet.networkIO(GuiInterface.SAVE,file)) { ModelFile = file; // Update configuration object TheConfiguration.setLastSavedDir(chooser.getDirectory()); } } } }); FileMenu.add(item); FileMenu.addSeparator(); item = new JMenuItem("Exit"); ks = KeyStroke.getKeyStroke('X', java.awt.Event.CTRL_MASK); item.setAccelerator(ks); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { close(); } }); FileMenu.add(item); MainMenu.add(FileMenu); // Make the RunMenu RunMenu = new JMenu("Run"); RunMenu.setBackground(Color.lightGray); item = new JMenuItem("Run Model"); ks = KeyStroke.getKeyStroke('R', java.awt.Event.CTRL_MASK); item.setAccelerator(ks); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { TheGeneNet.runControl(GuiInterface.RUN_MODEL); } }); RunMenu.add(item); item = new JMenuItem("Step Model"); ks = KeyStroke.getKeyStroke('G', java.awt.Event.CTRL_MASK); item.setAccelerator(ks); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { TheGeneNet.runControl(GuiInterface.STEP_MODEL); } }); RunMenu.add(item); item = new JMenuItem("Reset Model"); ks = KeyStroke.getKeyStroke('H', java.awt.Event.CTRL_MASK); item.setAccelerator(ks); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { TheGeneNet.runControl(GuiInterface.RESET_MODEL); } }); RunMenu.add(item); RunMenu.addSeparator(); item = new JMenuItem("Options"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { new OptionsDlog(); } }); RunMenu.add(item); MainMenu.add(RunMenu); // Make the Editors menu EditMenu = new JMenu("Editors"); EditMenu.setBackground(Color.lightGray); item = new JMenuItem("Model State Editor"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (GeneNet.TheModel!=null) { ModelStateEditor modelStateEditor = new ModelStateEditor(GeneNet.TheModel,SaveThis); addInternalFrame(modelStateEditor); addGUIListener(modelStateEditor); } } }); EditMenu.add(item); item = new JMenuItem("Model File Viewer"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (GeneNet.TheGeneNet!=null) { FileEditor editor = new FileEditor(GeneNet.TheGeneNet.getNetworkFile()); addInternalFrame(editor); } } }); EditMenu.add(item); MainMenu.add(EditMenu); // Make the viewers menu ViewMenu = new JMenu("Viewers"); ViewMenu.setBackground(Color.lightGray); item = new JMenuItem("Cell Viewer"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (GeneNet.TheModel!=null) { ModelState state = GeneNet.TheModel.getModelState(); if (state==null) { state = new ModelState(GeneNet.TheModel); GeneNet.TheModel.setModelState(state); } CellSurfaceViewer viewer = new CellSurfaceViewer(GeneNet.TheModel,SaveThis,"Cell Viewer",false); viewer.setModelState(state); state.addModelStateChangeListener(viewer); addInternalFrame(viewer); addGUIListener(viewer); } } }); ViewMenu.add(item); item = new JMenuItem("Node Viewer"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (GeneNet.TheModel!=null) { ModelState state = GeneNet.TheModel.getModelState(); if (state==null) { state = new ModelState(GeneNet.TheModel); GeneNet.TheModel.setModelState(state); } NodeStateViewer viewer = new NodeStateViewer(state,SaveThis,"Node Viewer"); state.addModelStateChangeListener(viewer); addInternalFrame(viewer); addGUIListener(viewer); } } }); ViewMenu.add(item); item = new JMenuItem("Network Viewer"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (GeneNet.TheModel!=null) { ModelState state = GeneNet.TheModel.getModelState(); if (state==null) { state = new ModelState(GeneNet.TheModel); GeneNet.TheModel.setModelState(state); } NetworkViewer2 viewer = new NetworkViewer2(TheGeneNet.getModel(),SaveThis); state.addModelStateChangeListener(viewer); addInternalFrame(viewer); addGUIListener(viewer); } } }); ViewMenu.add(item); item = new JMenuItem("Network Parameters"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { ParameterViewer viewer = new ParameterViewer(SaveThis,"Parameter Viewer",false); addInternalFrame(viewer); addGUIListener(viewer); } }); ViewMenu.add(item); item = new JMenuItem("Phase Portrait"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { PhasePortraitDlog phase = new PhasePortraitDlog(GeneNet.TheModel.net,GeneNet.TheModel); addInternalFrame(phase); } }); ViewMenu.add(item); item = new JMenuItem("Time Graph"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (GeneNet.TheModel!=null) { ModelState state = GeneNet.TheModel.getModelState(); if (state==null) { state = new ModelState(GeneNet.TheModel); GeneNet.TheModel.setModelState(state); } TimeGraph graph = new TimeGraph(GeneNet.TheModel, SaveThis); graph.setModelState(state); state.addModelStateChangeListener(graph); addInternalFrame(graph); addGUIListener(graph); } } }); ViewMenu.add(item); ViewMenu.addSeparator(); item = new JMenuItem("Output Console"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { showInternalFrame(TheConsole.getFrame()); System.setOut(TheConsole); } }); ViewMenu.add(item); item = new JMenuItem("Error Console"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { showInternalFrame(TheErrorConsole.getFrame()); System.setErr(TheErrorConsole); } }); ViewMenu.add(item); MainMenu.add(ViewMenu); // Make the help menu HelpMenu = new JMenu("Help"); HelpMenu.setBackground(Color.lightGray); item = new JMenuItem("Affectors"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { showInternalFrame(TheHelpWindow); TheHelpWindow.setURL(URLIntro+System.getProperty("user.dir")+"/doc/index.html"); } }); HelpMenu.add(item); HelpMenu.addSeparator(); item = new JMenuItem("Update Notes"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { showInternalFrame(TheHelpWindow); TheHelpWindow.setURL(URLIntro+System.getProperty("user.dir")+"/doc/notes.html"); } }); HelpMenu.add(item); HelpMenu.addSeparator(); item = new JMenuItem("About Ingeneue"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { showInternalFrame(TheHelpWindow); TheHelpWindow.setURL(URLIntro+System.getProperty("user.dir")+"/doc/hello.html"); } }); HelpMenu.add(item); MainMenu.add(HelpMenu); setJMenuBar(MainMenu); // Build the toolbar JToolBar MainToolbar = new JToolBar(); MainToolbar.setBackground(Color.lightGray); // Make a filler to push things to the right JPanel filler = new JPanel(); filler.setOpaque(false); MainToolbar.add(filler); // Make Run button RunButton = new JToggleButton("RUN"); RunButton.setFont(new Font("Arial",Font.BOLD,10)); RunButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (RunButton.isSelected()) TheGeneNet.runControl(GuiInterface.RUN_MODEL); RunButton.setSelected(false); } }); MainToolbar.add(RunButton); // Make Step button StepButton = new JToggleButton("STEP"); StepButton.setFont(new Font("Arial",Font.BOLD,10)); StepButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (StepButton.isSelected()) TheGeneNet.runControl(GuiInterface.STEP_MODEL); StepButton.setSelected(false); } }); MainToolbar.add(StepButton); // Make Reset button ResetButton = new JToggleButton("RESET"); ResetButton.setFont(new Font("Arial",Font.BOLD,10)); ResetButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (ResetButton.isSelected()) TheGeneNet.runControl(GuiInterface.RESET_MODEL); ResetButton.setSelected(false); } }); MainToolbar.add(ResetButton); // Make time label and time display box TimeLabelButton = new JToggleButton(" TIME: "); TimeLabelButton.setFont(new Font("Arial",Font.BOLD,10)); TimeLabelButton.setBorderPainted(false); TimeLabelButton.setFocusPainted(false); TimeLabelButton.setBackground(Color.lightGray); MainToolbar.add(TimeLabelButton); TimeField = new JLabel("0.0"); TimeField.setHorizontalAlignment(SwingConstants.RIGHT); TimeField.setSize(new Dimension(60,14)); TimeField.setPreferredSize(new Dimension(60,14)); TimeField.setMinimumSize(new Dimension(60,14)); TimeField.setMaximumSize(new Dimension(60,14)); MainToolbar.add(TimeField); TopPanel.add(MainToolbar,BorderLayout.NORTH); // Add a window listener to capture closing events. addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent ae) { close(); } }); // Create an output console and add it in TheConsole = new Console("Output",System.out); JInternalFrame consoleFrame = TheConsole.getFrame(); consoleFrame.setSize(600,100); // Create an error console and add it in TheErrorConsole = new Console("Errors",System.err); JInternalFrame errorConsoleFrame = TheErrorConsole.getFrame(); errorConsoleFrame.setSize(600,100); // Create the help window try { URL url = new URL(DefaultHelpURLString); TheHelpWindow = new HTMLViewer(url); addInternalFrame(TheHelpWindow); TheHelpWindow.setVisible(true); } catch (MalformedURLException mufe) { } // Make the display as big as possible ... almost. Dimension d = Toolkit.getDefaultToolkit().getScreenSize(); setBounds(0,0,d.width-40,d.height-60); setVisible(true); autoarrange(); TheConfiguration = new Configuration(); try { XMLDecoder decoder = new XMLDecoder(new BufferedInputStream(new FileInputStream("configuration.xml"))); TheConfiguration = (Configuration)decoder.readObject(); decoder.close(); // Now autoload things if not null if (TheConfiguration.getModelFile()!=null) { openAFile(TheConfiguration.getLastOpenedDir(),TheConfiguration.getModelFile()); } if (TheConfiguration.getIteratorFile()!=null) { openAFile(TheConfiguration.getLastOpenedDir(),TheConfiguration.getIteratorFile()); } } catch (Exception ex) { System.out.println("Failed to load configuration.xml - "+ex); } } public void openAFile(String path, String fileName) { File file = new File(path+fileName); // If file to be loaded is a model and load was successfull, then we // want to clear the GUI and invoke some new windows if (TheGeneNet.networkIO(GuiInterface.IS_MODEL,file)) { if (TheGeneNet.networkIO(GuiInterface.OPEN,file)) { ModelFile = file; clearGUI(); // Update the local model pointer TheModel = TheGeneNet.getModel(); // Create a new ModelState for the new Model ModelState state = new ModelState(TheGeneNet.getModel()); TheGeneNet.getModel().setModelState(state); state.addModelStateChangeListener(SaveThis); // Create a NetworkViewer and CellSurfaceViewer NetworkViewer2 netViewer = new NetworkViewer2(TheGeneNet.getModel(),SaveThis); state.addModelStateChangeListener(netViewer); addInternalFrame(netViewer); setAtDefaultLocation(netViewer); addGUIListener(netViewer); CellSurfaceViewer viewer = new CellSurfaceViewer(TheGeneNet.getModel(),SaveThis,"Cell Viewer",false); viewer.setModelState(state); state.addModelStateChangeListener(viewer); addInternalFrame(viewer); setAtDefaultLocation(viewer); addGUIListener(viewer); // Update configuration object TheConfiguration.setLastOpenedDir(path); TheConfiguration.setModelFile(fileName); } // If file to be loaded is an iterator and load was successfull, then we // want to create a new Iterator Viewer window. } else if (TheGeneNet.networkIO(GuiInterface.IS_ITERATOR,file)) { if (TheGeneNet.networkIO(GuiInterface.OPEN,file)) { IteratorViewer viewer = new IteratorViewer(TheGeneNet.getIterator(),SaveThis); addInternalFrame(viewer); // Update configuration object TheConfiguration.setLastOpenedDir(path); TheConfiguration.setIteratorFile(fileName); } } else { TheGeneNet.networkIO(GuiInterface.OPEN,file); } } /** @author WJS */ public void addGUIListener(GuiInterface listener) { if (listener!=null) GUIListeners.add(listener); } /** Add an internal frame to the GUI. @param JInternalFrame frame - JInternalFrame to be added. @author WJS */ public void addInternalFrame(JInternalFrame frame) { MainPanel.add(frame,0); } /** Places various Desktop comononents in nice starting places TheConsole - Lower left TheError Console Lower left The Help Window - Upper right @author WJS */ public void autoarrange() { TheConsole.setLocation(0,MainPanel.getHeight()-TheErrorConsole.getHeight()-TheConsole.getHeight()); TheErrorConsole.setLocation(0,MainPanel.getHeight()-TheErrorConsole.getHeight()); TheHelpWindow.setLocation(MainPanel.getWidth()-TheHelpWindow.getWidth(),0); } /** Clears all viewers, editors, etc. from the GUI. Only the consoles and the help window are left in place. Should be called before load operations and as part of close operations. @author WJS */ public void clearGUI() { // Clear the listeners GUIListeners = new Vector(); // Clear the viewers/editors/etc. Component[] c = MainPanel.getComponents(); for (int i=0;iMainPanel.getHeight()) r.y = MainPanel.getHeight()-r.height; // Off the right-side? if (r.x+r.width>MainPanel.getWidth()) r.x = MainPanel.getWidth()-r.width; frame.setLocation(r.x,r.y); } /******************** GUItoNet Interface implementation *********************/ /** Called when a change is made to the passed AffectorTemplate @param int operation @param AffectorTemplate affectorTemplate @param GuiInterface source - Originator of this interface change. @author WJS */ public void affectorTemplateChanged(int operation, AffectorTemplate affectorTemplate, GuiInterface source) { TheGeneNet.affectorTemplateChanged(operation,affectorTemplate,this); // Notify all other interface components GuiInterface gi; Enumeration en = GUIListeners.elements(); while (en.hasMoreElements()) { gi = (GuiInterface)en.nextElement(); if (gi!=source) gi.affectorTemplateChanged(operation, affectorTemplate, source); } } /** Method for invoking general network IO operations. @param int operation - The network IO operation code. @param File file - The file if needed, else don't care. @author WJS */ public boolean networkIO(int operation, File file) { boolean result = true; return(result); } /** Called when a change is made to the passed NodeTemplate @param int operation @param NodeTemplate nodeTemplate @param GuiInterface source - Originator of this interface change. @author WJS */ public void nodeTemplateChanged(int operation, NodeTemplate nodeTemplate, GuiInterface source) { TheGeneNet.nodeTemplateChanged(operation,nodeTemplate,this); // Notify all other interface components GuiInterface gi; Enumeration en = GUIListeners.elements(); while (en.hasMoreElements()) { gi = (GuiInterface)en.nextElement(); if (gi!=source) gi.nodeTemplateChanged(operation, nodeTemplate, source); } } /** Called when a change is made to a parameter or the whole parameter set @param int operation @param int parameterNumber @param GuiInterface source - Originator of this interface change. @author WJS */ public void parameterSetChanged(int operation, int parameterNumber, GuiInterface source) { TheGeneNet.parameterSetChanged(operation,parameterNumber,this); // Notify all other interface components GuiInterface gi; Enumeration en = GUIListeners.elements(); while (en.hasMoreElements()) { gi = (GuiInterface)en.nextElement(); if (gi==null) { System.out.println("gi is null"); Thread.dumpStack(); } if (source==null) { System.out.println("source is null"); Thread.dumpStack(); } if (gi!=source) gi.parameterSetChanged(operation,parameterNumber,source); } } /** Method for controling model/iterator runs. @param int operation - Run control code. @author WJS */ public void runControl(int operation) { } /** Method for reporting status of the model/iterator runs @param int operation - Run status code. @author WJS */ public void runStatus(int operation) { GuiInterface gi; Enumeration en = GUIListeners.elements(); while (en.hasMoreElements()) { gi = (GuiInterface)en.nextElement(); gi.runStatus(operation); } } /**************** ModelStateChange Interface implementation *****************/ /** Indicates that the entire ModelState has been updated * This method slows things down a LOT because of the constant repainting of * the time display. Either disable this or set it to update less often if * time is an issue. * Kerry changed the time-Lastime > to 100 from 5 @author WJS */ public void updateAll() { if (!TimeLabelButton.isSelected()) { // Uptime the Time step display. try { float time = TheModel.getModelState().getTime(); if (Math.abs(time-LastTime)>100) { // KK changed this to 100 from 5 TimeField.setText(Float.toString(((int)(time*100f))/100f)); LastTime = time; } } catch (NullPointerException ex) { // Do nothing } } } /** Updates only the indicated node. @param int cellIndex - The index of the cell with the node to be changed. @param int nodeIndex - The index of the node to be changed. @param float value - Thhe new value for the node. */ public void updateNode(int cellIndex, int nodeIndex, float value) { } }