package genegui; import java.awt.*; import java.awt.event.*; import javax.swing.*; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.Vector; import javax.swing.event.InternalFrameAdapter; import javax.swing.event.InternalFrameEvent; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import main.AffectorTemplate; import main.GeneNet; import main.Model; import main.NodeTemplate; /** TimeGraph.java A spanking new GUI component for collection and displaying time vs concentration graphs. TimeGraph will collect all data from a run and store it. The user then can select which data they want to display on the graph. @author WJS */ public class TimeGraph extends JInternalFrame implements GuiInterface, ModelStateChangeListener, OptionsPanelInterface { /** JToggleButton for turning auto-resizing function on and off */ private JToggleButton AutoResizeJButton; /** Debounce timer used for auto-resizing */ private javax.swing.Timer AutoResizeDebounceTimer; /** Preferred dimension for the CellPaintPanel */ private Dimension CellPaintPanelDimension; /** Stores the base cell shapes and locations */ private Polygon[] CellShapeArray; /** An ArrayList of matricies corresponding to each time step. X coordinant are cell # and Y coordinant node # */ private ArrayList DataArrays; /** When true allows data points to be added */ private boolean DataCollectionEnable = true; /** A collection of DataArrays for each model run */ private ArrayList DataSets; /** JLabel for showing the number of data sets */ private JLabel DataSetsDisplay; /** An ArrayList of Boolean which indicates if the graph hold button is on */ private ArrayList DataSetHoldArray; /** Hold button for indicating that a graph should be kept displayed despite value of DataSetIndex */ private JToggleButton DataSetHoldButton; /** The currently displayed data set index */ private int DataSetIndex = -1; /** Field for displaying and changing the current DataSet index */ private JTextField DataSetIndexDisplay; /** Global grid spacing */ private int GlobalGridSpacing = 10; /** Global legend setting */ private boolean GlobalLegendEnabled = true; /** Global show grid setting */ private boolean GlobalShowGrid = false; /** Global show range setting */ private boolean GlobalShowRange = true; /** Global graph XScale (width) setting (pixels/unit time) */ private float GlobalXScale = 1f; /** Global graph YScale (height) setting (pixels/unit value) */ private float GlobalYScale = 100f; /** The JPanel for storing GraphPanels */ private JPanel GraphsPanel; /** The ScrollPane that holds the GraphsPanel */ private JScrollPane GraphsScrollPane; /** The Main JMenuBar */ private JMenuBar MainMenu; /** The MainPanel is the content pane for the InternalFrame */ private JPanel MainPanel; /** The number of cells in the model */ private int NumberOfCells; /** The number of nodes in the network */ private int NumberOfNodes; /** If set successive data sets will not be accumulated, but will be over- write the current one and the data re-displayed. */ private JToggleButton OverWriteDataButton; /** Self reference */ private TimeGraph SaveThis = this; /** The scale at which to draw the cell shape objects */ private double Scale = 5.0; /** If set to true, simple display mode is used which just shows values */ private boolean SimpleDisplay = false; /** The GraphSurceSelector JFrame shared by all GraphPanels */ private GraphSourceSelector SourceSelector; /** The MainGui object that this belongs to */ private MainGui TheMainGui; /** A pointer to the Model being graphed */ private Model TheModel = null; /** A pointer to the ModelState used to pass information */ private ModelState TheModelState = null; /** An ArrayList of Floats with time values corresponsing to set of values in the DataArray */ private ArrayList TimeArray; /** Field for displaying number of datasets collected */ private JLabel TimePointsDisplay; /** A collection of TimeArrays for each model run */ private ArrayList TimeSets; /** Translation for drawing of cells */ private Point Translation = new Point(5,5); /** Main Constructor * @param Model model * @param MainGui theMainGui @author WJS */ public TimeGraph(Model model, MainGui theMainGui) { super("Time Graph",true,true,true,true); DataSets = new ArrayList(); TimeSets = new ArrayList(); DataArrays = new ArrayList(); TimeArray = new ArrayList(); DataSetHoldArray = new ArrayList(); TheModel = model; TheMainGui = theMainGui; NumberOfCells = TheModel.numCells; NumberOfNodes = TheModel.getNetwork().numNodes; TheModelState = TheModel.getModelState(); // Build the main panel MainPanel = new JPanel(); MainPanel.setLayout(new BorderLayout()); setContentPane(MainPanel); GraphsPanel = new JPanel(); GraphsPanel.setBackground(Color.white); GraphsPanel.setLayout(new BoxLayout(GraphsPanel,BoxLayout.Y_AXIS)); // Add one GraphPanel to the MainPanel to get the party started GraphPanel graph = new GraphPanel(); GraphsPanel.add(graph,BorderLayout.CENTER); // Add the GraphsPanel GraphsScrollPane = new JScrollPane(GraphsPanel); MainPanel.add(GraphsScrollPane); // Build the cell shape array for use by paint panels CellShapeArray = new Polygon[TheModelState.getNumberOfCells()]; Polygon p; Polygon q; for (int i=0;imaxX) maxX = rect.getX()+rect.getWidth(); if ((rect.getY()+rect.getHeight())>maxY) maxY = rect.getY()+rect.getHeight(); } CellPaintPanelDimension = new Dimension((int)maxX+20,(int)maxY+4); // Build the JMenuBar for the TimeGraph MainMenu = new JMenuBar(); MainMenu.setBackground(Color.lightGray); JMenuItem item; // File control JMenu fileMenu = new JMenu("File"); fileMenu.setBackground(Color.lightGray); item = new JMenuItem("Save Data Set"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { FileDialog chooser = new FileDialog(GeneNet.TheMainGui,"Choose File"); chooser.setDirectory(GeneNet.TheMainGui.getConfiguration().getLastSavedDir()); chooser.show(); if (chooser.getFile()!=null) { File file = new File(chooser.getDirectory()+chooser.getFile()); saveDataSets(file); GeneNet.TheMainGui.getConfiguration().setLastSavedDir(chooser.getDirectory()); } } }); fileMenu.add(item); MainMenu.add(fileMenu); // Graph control JMenu graphMenu = new JMenu("Graph"); graphMenu.setBackground(Color.lightGray); item = new JMenuItem("Add New Graph"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { GraphPanel graph = new GraphPanel(); GraphsPanel.add(graph); pack(); } }); graphMenu.add(item); item = new JMenuItem("Rebuild Graph"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { buildAll(); validate(); repaint(); } }); graphMenu.add(item); item = new JMenuItem("Remove All Graphs"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { GraphsPanel.removeAll(); repaint(); } }); graphMenu.add(item); graphMenu.addSeparator(); item = new JMenuItem("Clear Current Data Set"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { clearData(); buildAll(); repaint(); } }); graphMenu.add(item); item = new JMenuItem("Clear All Data Sets"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { clearAllDataSets(); buildAll(); repaint(); } }); graphMenu.add(item); item = new JMenuItem("Disable Data Collection"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { if (DataCollectionEnable) ((JMenuItem)ae.getSource()).setText("Enable Data Collection"); else ((JMenuItem)ae.getSource()).setText("Disable Data Collection"); DataCollectionEnable = !DataCollectionEnable; } }); graphMenu.add(item); graphMenu.addSeparator(); item = new JMenuItem("Options"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { OptionsPanel opt = new OptionsPanel(SaveThis,GlobalShowGrid,GlobalGridSpacing,GlobalLegendEnabled,GlobalShowRange,GlobalXScale,GlobalYScale); opt.setLocation(SaveThis.getLocationOnScreen()); } }); graphMenu.add(item); graphMenu.addSeparator(); item = new JMenuItem("Close"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { close(); } }); graphMenu.add(item); MainMenu.add(graphMenu); // Autographing functions control JMenu autoGraphMenu = new JMenu("Auto-graphs"); autoGraphMenu.setBackground(Color.lightGray); item = new JMenuItem("Graph Wizard"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { GraphWizard wizard = new GraphWizard(); } }); autoGraphMenu.add(item); JMenu quickSubMenu = new JMenu("Quick Graphs"); item = new JMenuItem("All nodes for one cell on one graph"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { } }); // quickSubMenu.add(item); item = new JMenuItem("All nodes for one cell on separate graphs"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { } }); // quickSubMenu.add(item); item = new JMenuItem("One node for all cells on one graph"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { } }); // quickSubMenu.add(item); item = new JMenuItem("One node for all cells on separate graphs"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { } }); // quickSubMenu.add(item); item = new JMenuItem("All nodes/All cells, one cell per graph"); item.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { GraphsPanel.removeAll(); GraphPanel graph; // Loop through all cells and create a graph panel for each for (int i=0;i1) green = 3f-4f*x; if (green<0) green = 0; float blue = x*2-1; if (blue<0) blue = 0; colors[i] = new Color((int)(255*red),(int)(255*green),(int)(255*blue)); x += 1f/(float)(n+1); } return(colors); } /** Removes a GraphPanel. @param GraphPanel panel - GraphPanel to be removed @author WJS */ private void removeGraphPanel(GraphPanel panel) { int height = GraphsPanel.getHeight()-panel.getHeight(); int width = GraphsPanel.getWidth(); GraphsPanel.remove(panel); GraphsPanel.setSize(width,height); GraphsPanel.validate(); GraphsPanel.repaint(); } /** Rescales X axis of all Graph panels to make them fit into the display windows current width. @author WJS */ private void reSizeGraphs() { if (TimeArray.size()==0) return; // Determine the maximum value for the time scale float MaxX = 0; for (int i=0;iTheMainGui.getContentPane().getHeight()-20) height = TheMainGui.getContentPane().getHeight()-20; setSize(width,height); } /** Reinitializes all the SingleNodeViewerPanels @author WJS */ private void reinitPanels() { Component c[] = GraphsPanel.getComponents(); for (int i=0;i=0)&&(ae2.getActionCommand().equals("Selected"))) { SourceSelector.setMode("Change"); SourceSelector.setActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae3) { SourceSelector.setVisible(false); if (ae3.getActionCommand().equals("Change")) { CellIndex = SourceSelector.getCellIndex(); NodeIndex = SourceSelector.getNodeIndex(); Color lineColor = SourceSelector.getLineColor(); String lineLabel = SourceSelector.getLineLabel(); DataPointers.set(SelectedLine,new DataPointer(CellIndex,NodeIndex)); Colors.set(SelectedLine,lineColor); Labels.set(SelectedLine,lineLabel); buildGraph(); } else if (ae3.getActionCommand().equals("Delete")) { removeDataPointer(SelectedLine); } SelectedLine = -1; validate(); repaint(); } }); DataPointer p = (DataPointer)DataPointers.get(SelectedLine); SourceSelector.setCellIndex(p.Cell); SourceSelector.setNodeIndex(p.Node); SourceSelector.setLineColor((Color)Colors.get(SelectedLine)); SourceSelector.setLineLabel((String)Labels.get(SelectedLine)); SourceSelector.setLocation(lChooserLocation); SourceSelector.pack(); SourceSelector.setVisible(true); } } }); LChooser.setLocation(changeGraphButton.getLocationOnScreen()); LChooser.setVisible(true); } }); p.add(changeGraphButton); // Make button for deleting a line graph final JButton deleteGraphButton = new JButton("Del"); deleteGraphButton.setMaximumSize(d); deleteGraphButton.setMinimumSize(d); deleteGraphButton.setPreferredSize(d); deleteGraphButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { // Create a quick JFrame for displaying a list of line graphs to choose // from. LChooser = new LineGraphChooser(Labels, Colors, new ActionListener() { public void actionPerformed(ActionEvent ae) { LChooser.dispose(); SelectedLine = ae.getID(); repaint(); if ((SelectedLine>=0)&&(ae.getActionCommand().equals("Selected"))) { removeDataPointer(SelectedLine); SelectedLine = -1; repaint(); } } }); LChooser.setLocation(deleteGraphButton.getLocationOnScreen()); LChooser.setVisible(true); } }); p.add(deleteGraphButton); // Make button for choosing graph options JButton optionsGraphButton = new JButton("Opt"); optionsGraphButton.setMaximumSize(d); optionsGraphButton.setMinimumSize(d); optionsGraphButton.setPreferredSize(d); optionsGraphButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { OptionsPanel opt = new OptionsPanel(SaveThis,ShowGrid,GridSpacing,LegendEnabled,ShowRange,GlobalXScale,YScale); opt.setLocation(((JButton)ae.getSource()).getLocationOnScreen()); } }); p.add(optionsGraphButton); // Make button for clearing all line graphs JButton clearButton = new JButton("Clr"); clearButton.setMaximumSize(d); clearButton.setMinimumSize(d); clearButton.setPreferredSize(d); clearButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae) { Colors = new ArrayList(); DataPointers = new ArrayList(); Labels = new ArrayList(); LegendAreas = new ArrayList(); YPaintArrays = new ArrayList(); for (int s=0;sYScale+TOP_SPACE) { // Scan legends for (int i=0;ip.x) { while (l>p.x) { x--; l = xScaleArray[x]; } if ((xScaleArray[x+1]-p.x)<(p.x-l)) x++; break; } if (lRejectLimit) index = -1; } return(index); } /** Returns the visible width of the Graphic PaintPanel. @author WJS */ public int getControlPanelWidth() { int width = ThePaintPanel.getLocation().x; return(width); } /** Removes a data pointer from the graph. @param int index - The DataPointer/Line index. @author WJS */ public void removeDataPointer(int index) { Colors.remove(index); DataPointers.remove(index); Labels.remove(index); LegendAreas.remove(index); ArrayList yArrays; for (int s=0;s=0) { SourceSelector.setMode("Change"); SourceSelector.setActionListener(new ActionListener() { public void actionPerformed(ActionEvent ae3) { SourceSelector.setVisible(false); if (ae3.getActionCommand().equals("Change")) { CellIndex = SourceSelector.getCellIndex(); NodeIndex = SourceSelector.getNodeIndex(); Color lineColor = SourceSelector.getLineColor(); String lineLabel = SourceSelector.getLineLabel(); DataPointers.set(SelectedLine,new DataPointer(CellIndex,NodeIndex)); Colors.set(SelectedLine,lineColor); Labels.set(SelectedLine,lineLabel); buildGraph(); } else if (ae3.getActionCommand().equals("Delete")) { removeDataPointer(SelectedLine); } SelectedLine = -1; GraphsPanel.validate(); GraphsPanel.repaint(); } }); DataPointer p = (DataPointer)DataPointers.get(SelectedLine); SourceSelector.setCellIndex(p.Cell); SourceSelector.setNodeIndex(p.Node); SourceSelector.setLineColor((Color)Colors.get(SelectedLine)); SourceSelector.setLineLabel((String)Labels.get(SelectedLine)); Point clickLocation = me.getPoint(); Point q = getLocationOnScreen(); clickLocation.translate(q.x,q.y); SourceSelector.setLocation(clickLocation); SourceSelector.pack(); SourceSelector.setVisible(true); } } }); // Set a default start size Dimension d = new Dimension((int)GlobalXScale*200,(int)YScale+LegendHeight); setSize(d); setPreferredSize(d); setMinimumSize(d); } /** Over-ride paint routine to allow painting of the lines graphs @param Graphics g0 @author WJS */ public void paint(Graphics g0) { super.paint(g0); Graphics2D g = (Graphics2D)g0; g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON); // Draw any Grids if (ShowGrid) { g0.setColor(GridColor); for (int i=0;i0) { int[] dataX; int[] dataY; ArrayList yPaintArrays; for (int s=0;s0) { // Draw the legend if so desired if (LegendEnabled) { g.setStroke(new BasicStroke(1)); FontMetrics fm = g0.getFontMetrics(); int x = 4; int xl = x; int y = (int)YScale+TOP_SPACE+20; Rectangle2D r; String s; for (int i=0;i<((ArrayList)YPaintArrays.get(DataSetIndex)).size();i++) { // Paint a square box with the color swatch in it g.setColor((Color)Colors.get(i)); g.fillRect(x,y-12,12,12); g.setColor(Color.black); g.drawRect(x,y-12,12,12); x = x+14; // Paint the label s = (String)Labels.get(i); g0.drawString(s,x,y); r = fm.getStringBounds(s,g0); x += r.getWidth()+4; // Set the click area for the legend LegendAreas.set(i,new Rectangle(xl,y-12,x-xl,12)); xl = x; } } } } } } /** DataPointer.java Class for defining a pointer into the DataSets @author WJS */ private class DataPointer { /** The Cell Index */ public int Cell = 0; /** The Node Index */ public int Node = 0; public DataPointer() { } public DataPointer(int cell, int node) { Cell = cell; Node = node; } } /** LineGraphChooser.java JFrame that allows users to select a line graph from the list of line graphs. @author WJS */ private class LineGraphChooser extends JFrame { /** ArrayList of colors for line graphs */ private ArrayList Colors; /** ActionListener */ private ActionListener Listener; /** ArrayList of labels for Line graphs */ private ArrayList Names; /** Self Reference */ private LineGraphChooser SaveThis = this; public LineGraphChooser(ArrayList names, ArrayList colors, ActionListener listener) { super("Line Graphs"); setResizable(false); setIconifiable(false); Listener = listener; Colors = colors; Names = names; JPanel mainFrame = new JPanel(); mainFrame.setLayout(new BoxLayout(mainFrame,BoxLayout.Y_AXIS)); Vector vList = new Vector(names); final JList graphList = new JList(vList); graphList.setCellRenderer(new LineGraphChooserCellRenderer()); graphList.addListSelectionListener(new ListSelectionListener() { public void valueChanged(ListSelectionEvent lse) { int index = graphList.getSelectedIndex(); Listener.actionPerformed(new ActionEvent(SaveThis,index,"Selected")); dispose(); } }); JScrollPane scrollPane = new JScrollPane(graphList); mainFrame.add(scrollPane); setContentPane(mainFrame); // Add a focus change listener to close the LineGraphChooser addWindowFocusListener(new WindowAdapter() { public void windowLostFocus(WindowEvent we) { dispose(); } }); setSize(200,200); setVisible(true); } /** LineGraphChooserCellRenderer.java Implementation of a ListCellRender for drawing elements in a LineGraphChooser JList. @author WJS */ private class LineGraphChooserCellRenderer extends JLabel implements ListCellRenderer { public LineGraphChooserCellRenderer() { setOpaque(true); setBackground(Color.white); } public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { setText((String)Names.get(index)); BufferedImage colorSwatchImage = new BufferedImage(14,14,BufferedImage.TYPE_INT_RGB); Graphics g = colorSwatchImage.getGraphics(); g.setColor((Color)Colors.get(index)); g.fillRect(0,0,14,14); g.setColor(Color.black); g.drawRect(0,0,13,13); ImageIcon colorSwatch = new ImageIcon(colorSwatchImage); setIcon(colorSwatch); return(this); } } } /** GraphSourceSelector.java Internal class produces a JFrame that permits the user to select a cell and node Index. User may also select the drawing color for the line and chooose a display label. @author WJS */ private class GraphSourceSelector extends JFrame { /** The index of the selected cell */ private int CellIndex = -1; /** ColorSwatch shows the current color for the line graph */ private JPanel ColorSwatch; /** The user selected color for the line graph */ private Color LineColor = Color.black; /** Users supplied label for line graph */ private String LineLabel = ""; /** ActionListener */ private ActionListener Listener; /** Mode Button used to complete operation */ private JButton ModeButton; /** The index of the selected node */ private int NodeIndex = -1; /** The JCombobox for selecting the node beging graphed */ private JComboBox NodeSelectionBox; /** Self reference */ private JFrame SaveThis = this; /** Set to true to prevent frame closing when focus lost */ private boolean SquelchCloseOnFocusLost = false; /** The panel the cell outlines are painted onto */ private CellPaintPanel TheCellPaintPanel; /** Simple constructor. @author WJS */ public GraphSourceSelector() { super("Graph Source"); setResizable(false); setIconifiable(false); JPanel mainPanel = new JPanel(); mainPanel.setLayout(new BoxLayout(mainPanel,BoxLayout.Y_AXIS)); setContentPane(mainPanel); // Make panel that shows the selected cell TheCellPaintPanel = new CellPaintPanel(); mainPanel.add(TheCellPaintPanel); // Create a JComboBox to allow users to select a node to be displayed NodeSelectionBox = new JComboBox(); NodeTemplate node; for (int i=0;i