| 1 | /* | 
|---|
| 2 |  *    This program is free software; you can redistribute it and/or modify | 
|---|
| 3 |  *    it under the terms of the GNU General Public License as published by | 
|---|
| 4 |  *    the Free Software Foundation; either version 2 of the License, or | 
|---|
| 5 |  *    (at your option) any later version. | 
|---|
| 6 |  * | 
|---|
| 7 |  *    This program is distributed in the hope that it will be useful, | 
|---|
| 8 |  *    but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 9 |  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 10 |  *    GNU General Public License for more details. | 
|---|
| 11 |  * | 
|---|
| 12 |  *    You should have received a copy of the GNU General Public License | 
|---|
| 13 |  *    along with this program; if not, write to the Free Software | 
|---|
| 14 |  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 
|---|
| 15 |  */ | 
|---|
| 16 |  | 
|---|
| 17 | /* | 
|---|
| 18 |  *    GraphVisualizer.java | 
|---|
| 19 |  *    Copyright (C) 2003 University of Waikato, Hamilton, New Zealand | 
|---|
| 20 |  * | 
|---|
| 21 |  */ | 
|---|
| 22 | package weka.gui.graphvisualizer; | 
|---|
| 23 |  | 
|---|
| 24 | import weka.core.FastVector; | 
|---|
| 25 | import weka.gui.ExtensionFileFilter; | 
|---|
| 26 | import weka.gui.visualize.PrintablePanel; | 
|---|
| 27 |  | 
|---|
| 28 | import java.awt.BorderLayout; | 
|---|
| 29 | import java.awt.Color; | 
|---|
| 30 | import java.awt.Container; | 
|---|
| 31 | import java.awt.Dimension; | 
|---|
| 32 | import java.awt.Font; | 
|---|
| 33 | import java.awt.FontMetrics; | 
|---|
| 34 | import java.awt.Frame; | 
|---|
| 35 | import java.awt.Graphics; | 
|---|
| 36 | import java.awt.Graphics2D; | 
|---|
| 37 | import java.awt.GridBagConstraints; | 
|---|
| 38 | import java.awt.GridBagLayout; | 
|---|
| 39 | import java.awt.Insets; | 
|---|
| 40 | import java.awt.LayoutManager; | 
|---|
| 41 | import java.awt.Rectangle; | 
|---|
| 42 | import java.awt.RenderingHints; | 
|---|
| 43 | import java.awt.event.ActionEvent; | 
|---|
| 44 | import java.awt.event.ActionListener; | 
|---|
| 45 | import java.awt.event.MouseAdapter; | 
|---|
| 46 | import java.awt.event.MouseEvent; | 
|---|
| 47 | import java.awt.event.MouseMotionAdapter; | 
|---|
| 48 | import java.io.FileInputStream; | 
|---|
| 49 | import java.io.FileReader; | 
|---|
| 50 | import java.io.IOException; | 
|---|
| 51 | import java.io.InputStream; | 
|---|
| 52 | import java.io.Reader; | 
|---|
| 53 |  | 
|---|
| 54 | import javax.swing.BorderFactory; | 
|---|
| 55 | import javax.swing.ImageIcon; | 
|---|
| 56 | import javax.swing.JButton; | 
|---|
| 57 | import javax.swing.JCheckBox; | 
|---|
| 58 | import javax.swing.JDialog; | 
|---|
| 59 | import javax.swing.JFileChooser; | 
|---|
| 60 | import javax.swing.JFrame; | 
|---|
| 61 | import javax.swing.JLabel; | 
|---|
| 62 | import javax.swing.JOptionPane; | 
|---|
| 63 | import javax.swing.JPanel; | 
|---|
| 64 | import javax.swing.JScrollPane; | 
|---|
| 65 | import javax.swing.JTable; | 
|---|
| 66 | import javax.swing.JTextField; | 
|---|
| 67 | import javax.swing.JToolBar; | 
|---|
| 68 | import javax.swing.table.AbstractTableModel; | 
|---|
| 69 |  | 
|---|
| 70 | /** | 
|---|
| 71 |  * This class displays the graph we want to visualize. It should | 
|---|
| 72 |  * be sufficient to use only this class in weka.gui.graphvisulizer | 
|---|
| 73 |  * package to visualize a graph. The description of a graph should | 
|---|
| 74 |  * be provided as a string argument using readBIF or readDOT method | 
|---|
| 75 |  * in either XMLBIF03 or DOT format. Alternatively, an InputStream | 
|---|
| 76 |  * in XMLBIF03 can also be provided to another variation of readBIF. | 
|---|
| 77 |  * It would be necessary in case input is in DOT format to call the | 
|---|
| 78 |  * layoutGraph() method to display the graph correctly after the call | 
|---|
| 79 |  * to readDOT. It is also necessary to do so if readBIF is called and | 
|---|
| 80 |  * the graph description doesn't have x y positions for nodes. | 
|---|
| 81 |  * <p> The graph's data is held in two FastVectors, nodes are stored as | 
|---|
| 82 |  * objects of GraphNode class and edges as objects of GraphEdge class. | 
|---|
| 83 |  * <p> The graph is displayed by positioning and drawing each node | 
|---|
| 84 |  * according to its x y position and then drawing all the edges coming | 
|---|
| 85 |  * out of it give by its edges[][] array, the arrow heads are ofcourse | 
|---|
| 86 |  * marked in the opposite(ie original direction) or both directions if | 
|---|
| 87 |  * the edge is reversed or is in both directions. The graph is centered | 
|---|
| 88 |  * if it is smaller than it's display area. The edges are drawn from the | 
|---|
| 89 |  * bottom of the current node to the top of the node given by edges[][] | 
|---|
| 90 |  * array in GraphNode class, to avoid edges crossing over other nodes. | 
|---|
| 91 |  * This might need to be changed if another layout engine is added or | 
|---|
| 92 |  * the current Hierarchical engine is updated to avoid such crossings | 
|---|
| 93 |  * over nodes. | 
|---|
| 94 |  * | 
|---|
| 95 |  * @author Ashraf M. Kibriya (amk14@cs.waikato.ac.nz) | 
|---|
| 96 |  * @version $Revision: 4723 $ | 
|---|
| 97 |  */ | 
|---|
| 98 | public class GraphVisualizer | 
|---|
| 99 |   extends JPanel | 
|---|
| 100 |   implements GraphConstants, LayoutCompleteEventListener { | 
|---|
| 101 |  | 
|---|
| 102 |   /** for serialization */ | 
|---|
| 103 |   private static final long serialVersionUID = -2038911085935515624L; | 
|---|
| 104 |    | 
|---|
| 105 |   /** Vector containing nodes */ | 
|---|
| 106 |   protected FastVector m_nodes=new FastVector(); | 
|---|
| 107 |   /** Vector containing edges */ | 
|---|
| 108 |   protected FastVector m_edges=new FastVector(); | 
|---|
| 109 |   /** The current LayoutEngine  */ | 
|---|
| 110 |   protected LayoutEngine m_le; | 
|---|
| 111 |   /** Panel actually displaying the graph */ | 
|---|
| 112 |   protected GraphPanel m_gp; | 
|---|
| 113 |   /** String containing graph's name */ | 
|---|
| 114 |   protected String graphID; | 
|---|
| 115 |    | 
|---|
| 116 |   /** | 
|---|
| 117 |    * Save button to save the current graph in DOT or XMLBIF format. | 
|---|
| 118 |    * The graph should be layed out again to get the original form | 
|---|
| 119 |    * if reloaded from command line, as the formats do not allow | 
|---|
| 120 |    * saving specific information for a properly layed out graph. | 
|---|
| 121 |    */ | 
|---|
| 122 |   protected JButton m_jBtSave; | 
|---|
| 123 |    | 
|---|
| 124 |   /** path for icons */ | 
|---|
| 125 |   private final String ICONPATH = "weka/gui/graphvisualizer/icons/"; | 
|---|
| 126 |    | 
|---|
| 127 |   private FontMetrics fm = this.getFontMetrics( this.getFont() ); | 
|---|
| 128 |   private double scale = 1;   //current zoom | 
|---|
| 129 |   private int nodeHeight = 2*fm.getHeight(), nodeWidth = 24; | 
|---|
| 130 |   private int paddedNodeWidth = 24+8; | 
|---|
| 131 |   /** TextField for node's width */ | 
|---|
| 132 |   private final JTextField jTfNodeWidth = new JTextField(3); | 
|---|
| 133 |   /** TextField for nodes height */ | 
|---|
| 134 |   private final JTextField jTfNodeHeight = new JTextField(3); | 
|---|
| 135 |   /** Button for laying out the graph again, necessary after changing node's | 
|---|
| 136 |    * size or some other property of the layout engine | 
|---|
| 137 |    */ | 
|---|
| 138 |   private final JButton jBtLayout; | 
|---|
| 139 |   /** used for setting appropriate node size */ | 
|---|
| 140 |   private int maxStringWidth=0; | 
|---|
| 141 |   /** used when using zoomIn and zoomOut buttons */ | 
|---|
| 142 |   private int [] zoomPercents = { 10, 25, 50, 75, 100, 125, 150, 175, 200, 225, | 
|---|
| 143 |   250, 275, 300, 350, 400, 450, 500, 550, 600, 650, 700, 800, 900, 999 }; | 
|---|
| 144 |   /** this contains the m_gp GraphPanel */ | 
|---|
| 145 |   JScrollPane m_js; | 
|---|
| 146 |    | 
|---|
| 147 |   /** | 
|---|
| 148 |    * Constructor<br> | 
|---|
| 149 |    * Sets up the gui and initializes all the other previously | 
|---|
| 150 |    * uninitialized variables. | 
|---|
| 151 |    */ | 
|---|
| 152 |   public GraphVisualizer() { | 
|---|
| 153 |     m_gp = new GraphPanel(); | 
|---|
| 154 |     m_js = new JScrollPane(m_gp); | 
|---|
| 155 |      | 
|---|
| 156 |     //creating a new layout engine and adding this class as its listener | 
|---|
| 157 |     // to receive layoutComplete events | 
|---|
| 158 |     m_le=new HierarchicalBCEngine(m_nodes, m_edges,  | 
|---|
| 159 |                                   paddedNodeWidth, nodeHeight); | 
|---|
| 160 |     m_le.addLayoutCompleteEventListener(this); | 
|---|
| 161 |      | 
|---|
| 162 |     m_jBtSave = new JButton(); | 
|---|
| 163 |     java.net.URL tempURL = ClassLoader.getSystemResource(ICONPATH+"save.gif"); | 
|---|
| 164 |     if(tempURL!=null) | 
|---|
| 165 |       m_jBtSave.setIcon(new ImageIcon(tempURL) ); | 
|---|
| 166 |     else | 
|---|
| 167 |       System.err.println(ICONPATH+ | 
|---|
| 168 |       "save.gif not found for weka.gui.graphvisualizer.Graph"); | 
|---|
| 169 |     m_jBtSave.setToolTipText("Save Graph"); | 
|---|
| 170 |     m_jBtSave.addActionListener( new ActionListener() { | 
|---|
| 171 |       public void actionPerformed(ActionEvent ae) { | 
|---|
| 172 |         JFileChooser fc = new JFileChooser(System.getProperty("user.dir")); | 
|---|
| 173 |         ExtensionFileFilter ef1 = new ExtensionFileFilter(".dot", "DOT files"); | 
|---|
| 174 |         ExtensionFileFilter ef2 = new ExtensionFileFilter(".xml", | 
|---|
| 175 |         "XML BIF files"); | 
|---|
| 176 |         fc.addChoosableFileFilter(ef1); | 
|---|
| 177 |         fc.addChoosableFileFilter(ef2); | 
|---|
| 178 |         fc.setDialogTitle("Save Graph As"); | 
|---|
| 179 |         int rval = fc.showSaveDialog(GraphVisualizer.this); | 
|---|
| 180 |          | 
|---|
| 181 |         if (rval == JFileChooser.APPROVE_OPTION) { | 
|---|
| 182 |           //System.out.println("Saving to file \""+ | 
|---|
| 183 |           //                   f.getAbsoluteFile().toString()+"\""); | 
|---|
| 184 |           if(fc.getFileFilter()==ef2) { | 
|---|
| 185 |             String filename = fc.getSelectedFile().toString(); | 
|---|
| 186 |             if(!filename.endsWith(".xml")) | 
|---|
| 187 |               filename = filename.concat(".xml"); | 
|---|
| 188 |             BIFParser.writeXMLBIF03(filename, graphID, m_nodes, m_edges); | 
|---|
| 189 |           } | 
|---|
| 190 |           else { | 
|---|
| 191 |             String filename = fc.getSelectedFile().toString(); | 
|---|
| 192 |             if(!filename.endsWith(".dot")) | 
|---|
| 193 |               filename = filename.concat(".dot"); | 
|---|
| 194 |             DotParser.writeDOT(filename, graphID, m_nodes, m_edges); | 
|---|
| 195 |           } | 
|---|
| 196 |         } | 
|---|
| 197 |       } | 
|---|
| 198 |     }); | 
|---|
| 199 |      | 
|---|
| 200 |     final JButton jBtZoomIn = new JButton(); | 
|---|
| 201 |     tempURL = ClassLoader.getSystemResource(ICONPATH+"zoomin.gif"); | 
|---|
| 202 |     if(tempURL!=null) | 
|---|
| 203 |       jBtZoomIn.setIcon(new ImageIcon(tempURL) ); | 
|---|
| 204 |     else | 
|---|
| 205 |       System.err.println(ICONPATH+ | 
|---|
| 206 |       "zoomin.gif not found for weka.gui.graphvisualizer.Graph"); | 
|---|
| 207 |     jBtZoomIn.setToolTipText("Zoom In"); | 
|---|
| 208 |      | 
|---|
| 209 |     final JButton jBtZoomOut = new JButton(); | 
|---|
| 210 |     tempURL = ClassLoader.getSystemResource(ICONPATH+"zoomout.gif"); | 
|---|
| 211 |     if(tempURL!=null) | 
|---|
| 212 |       jBtZoomOut.setIcon(new ImageIcon(tempURL) ); | 
|---|
| 213 |     else | 
|---|
| 214 |       System.err.println(ICONPATH+ | 
|---|
| 215 |       "zoomout.gif not found for weka.gui.graphvisualizer.Graph"); | 
|---|
| 216 |     jBtZoomOut.setToolTipText("Zoom Out"); | 
|---|
| 217 |      | 
|---|
| 218 |     final JTextField jTfZoom = new JTextField("100%"); | 
|---|
| 219 |     jTfZoom.setMinimumSize( jTfZoom.getPreferredSize() ); | 
|---|
| 220 |     jTfZoom.setHorizontalAlignment(JTextField.CENTER); | 
|---|
| 221 |     jTfZoom.setToolTipText("Zoom"); | 
|---|
| 222 |      | 
|---|
| 223 |     jTfZoom.addActionListener( new ActionListener() { | 
|---|
| 224 |       public void actionPerformed(ActionEvent ae) { | 
|---|
| 225 |         JTextField jt = (JTextField)ae.getSource(); | 
|---|
| 226 |         try { | 
|---|
| 227 |           int i=-1; | 
|---|
| 228 |           i = jt.getText().indexOf('%'); | 
|---|
| 229 |           if(i==-1) | 
|---|
| 230 |             i = Integer.parseInt(jt.getText()); | 
|---|
| 231 |           else | 
|---|
| 232 |             i = Integer.parseInt(jt.getText().substring(0,i)); | 
|---|
| 233 |            | 
|---|
| 234 |           if(i<=999) | 
|---|
| 235 |             scale = i/100D; | 
|---|
| 236 |            | 
|---|
| 237 |           jt.setText((int)(scale*100)+"%"); | 
|---|
| 238 |            | 
|---|
| 239 |           if(scale>0.1){ | 
|---|
| 240 |             if(!jBtZoomOut.isEnabled()) | 
|---|
| 241 |               jBtZoomOut.setEnabled(true); | 
|---|
| 242 |           } | 
|---|
| 243 |           else | 
|---|
| 244 |             jBtZoomOut.setEnabled(false); | 
|---|
| 245 |           if(scale<9.99) { | 
|---|
| 246 |             if(!jBtZoomIn.isEnabled()) | 
|---|
| 247 |               jBtZoomIn.setEnabled(true); | 
|---|
| 248 |           } | 
|---|
| 249 |           else | 
|---|
| 250 |             jBtZoomIn.setEnabled(false); | 
|---|
| 251 |            | 
|---|
| 252 |           setAppropriateSize(); | 
|---|
| 253 |           //m_gp.clearBuffer(); | 
|---|
| 254 |           m_gp.repaint(); | 
|---|
| 255 |           m_gp.invalidate(); | 
|---|
| 256 |           m_js.revalidate(); | 
|---|
| 257 |         } catch(NumberFormatException ne) { | 
|---|
| 258 |           JOptionPane.showMessageDialog(GraphVisualizer.this.getParent(), | 
|---|
| 259 |           "Invalid integer entered for zoom.", | 
|---|
| 260 |           "Error", | 
|---|
| 261 |           JOptionPane.ERROR_MESSAGE); | 
|---|
| 262 |           jt.setText((scale*100)+"%"); | 
|---|
| 263 |         } | 
|---|
| 264 |       } | 
|---|
| 265 |     }); | 
|---|
| 266 |      | 
|---|
| 267 |      | 
|---|
| 268 |     jBtZoomIn.addActionListener( new ActionListener() { | 
|---|
| 269 |       public void actionPerformed(ActionEvent ae) { | 
|---|
| 270 |         int i=0, s = (int)(scale*100); | 
|---|
| 271 |         if(s<300) | 
|---|
| 272 |           i = s/25; | 
|---|
| 273 |         else if(s<700) | 
|---|
| 274 |           i = 6 +  s/50; | 
|---|
| 275 |         else | 
|---|
| 276 |           i = 13 +s/100; | 
|---|
| 277 |          | 
|---|
| 278 |         if(s>=999) { | 
|---|
| 279 |           JButton b = (JButton)ae.getSource(); | 
|---|
| 280 |           b.setEnabled(false); | 
|---|
| 281 |           return; | 
|---|
| 282 |         } | 
|---|
| 283 |         else if(s>=10){ | 
|---|
| 284 |           if(i>=22) { | 
|---|
| 285 |             JButton b = (JButton)ae.getSource(); | 
|---|
| 286 |             b.setEnabled(false); | 
|---|
| 287 |           } | 
|---|
| 288 |           if(s==10 && !jBtZoomOut.isEnabled()) | 
|---|
| 289 |             jBtZoomOut.setEnabled(true); | 
|---|
| 290 |           //System.out.println("i: "+i+"Zoom is: "+zoomPercents[i+1]); | 
|---|
| 291 |           jTfZoom.setText(zoomPercents[i+1]+"%"); | 
|---|
| 292 |           scale = zoomPercents[i+1]/100D; | 
|---|
| 293 |         } | 
|---|
| 294 |         else { | 
|---|
| 295 |           if(!jBtZoomOut.isEnabled()) | 
|---|
| 296 |             jBtZoomOut.setEnabled(true); | 
|---|
| 297 |           //System.out.println("i: "+i+"Zoom is: "+zoomPercents[0]); | 
|---|
| 298 |           jTfZoom.setText(zoomPercents[0]+"%"); | 
|---|
| 299 |           scale = zoomPercents[0]/100D; | 
|---|
| 300 |         } | 
|---|
| 301 |         setAppropriateSize(); | 
|---|
| 302 |         m_gp.repaint(); | 
|---|
| 303 |         m_gp.invalidate(); | 
|---|
| 304 |         m_js.revalidate(); | 
|---|
| 305 |       } | 
|---|
| 306 |     }); | 
|---|
| 307 |      | 
|---|
| 308 |      | 
|---|
| 309 |     jBtZoomOut.addActionListener( new ActionListener() { | 
|---|
| 310 |       public void actionPerformed(ActionEvent ae) { | 
|---|
| 311 |         int i=0, s = (int)(scale*100); | 
|---|
| 312 |         if(s<300) | 
|---|
| 313 |           i = (int) Math.ceil(s/25D); | 
|---|
| 314 |         else if(s<700) | 
|---|
| 315 |           i = 6 +  (int) Math.ceil(s/50D); | 
|---|
| 316 |         else | 
|---|
| 317 |           i = 13 + (int) Math.ceil(s/100D); | 
|---|
| 318 |          | 
|---|
| 319 |         if(s<=10) { | 
|---|
| 320 |           JButton b = (JButton)ae.getSource(); | 
|---|
| 321 |           b.setEnabled(false); | 
|---|
| 322 |         } | 
|---|
| 323 |         else if(s<999) { | 
|---|
| 324 |           if(i<=1) { | 
|---|
| 325 |             JButton b = (JButton)ae.getSource(); | 
|---|
| 326 |             b.setEnabled(false); | 
|---|
| 327 |           } | 
|---|
| 328 |           //System.out.println("i: "+i+"Zoom is: "+zoomPercents[i-1]); | 
|---|
| 329 |           jTfZoom.setText(zoomPercents[i-1]+"%"); | 
|---|
| 330 |           scale = zoomPercents[i-1]/100D; | 
|---|
| 331 |         } | 
|---|
| 332 |         else{ | 
|---|
| 333 |           if(!jBtZoomIn.isEnabled()) | 
|---|
| 334 |             jBtZoomIn.setEnabled(true); | 
|---|
| 335 |           //System.out.println("i: "+i+"Zoom is: "+zoomPercents[22]); | 
|---|
| 336 |           jTfZoom.setText(zoomPercents[22]+"%"); | 
|---|
| 337 |           scale = zoomPercents[22]/100D; | 
|---|
| 338 |         } | 
|---|
| 339 |         setAppropriateSize(); | 
|---|
| 340 |         m_gp.repaint(); | 
|---|
| 341 |         m_gp.invalidate(); | 
|---|
| 342 |         m_js.revalidate(); | 
|---|
| 343 |       } | 
|---|
| 344 |     }); | 
|---|
| 345 |      | 
|---|
| 346 |      | 
|---|
| 347 |     //This button pops out the extra controls | 
|---|
| 348 |     JButton jBtExtraControls = new JButton(); | 
|---|
| 349 |     tempURL = ClassLoader.getSystemResource(ICONPATH+"extra.gif"); | 
|---|
| 350 |     if(tempURL!=null) | 
|---|
| 351 |       jBtExtraControls.setIcon(new ImageIcon(tempURL) ); | 
|---|
| 352 |     else | 
|---|
| 353 |       System.err.println(ICONPATH+ | 
|---|
| 354 |       "extra.gif not found for weka.gui.graphvisualizer.Graph"); | 
|---|
| 355 |     jBtExtraControls.setToolTipText("Show/Hide extra controls"); | 
|---|
| 356 |      | 
|---|
| 357 |      | 
|---|
| 358 |     final JCheckBox jCbCustomNodeSize = new JCheckBox("Custom Node Size"); | 
|---|
| 359 |     final JLabel jLbNodeWidth = new JLabel("Width"); | 
|---|
| 360 |     final JLabel jLbNodeHeight = new JLabel("Height"); | 
|---|
| 361 |      | 
|---|
| 362 |     jTfNodeWidth.setHorizontalAlignment(JTextField.CENTER); | 
|---|
| 363 |     jTfNodeWidth.setText(""+nodeWidth); | 
|---|
| 364 |     jTfNodeHeight.setHorizontalAlignment(JTextField.CENTER); | 
|---|
| 365 |     jTfNodeHeight.setText(""+nodeHeight); | 
|---|
| 366 |     jLbNodeWidth.setEnabled(false); | 
|---|
| 367 |     jTfNodeWidth.setEnabled(false); | 
|---|
| 368 |     jLbNodeHeight.setEnabled(false); | 
|---|
| 369 |     jTfNodeHeight.setEnabled(false); | 
|---|
| 370 |      | 
|---|
| 371 |     jCbCustomNodeSize.addActionListener( new ActionListener() { | 
|---|
| 372 |       public void actionPerformed(ActionEvent ae) { | 
|---|
| 373 |         if( ((JCheckBox)ae.getSource()).isSelected() ) { | 
|---|
| 374 |           jLbNodeWidth.setEnabled(true); | 
|---|
| 375 |           jTfNodeWidth.setEnabled(true); | 
|---|
| 376 |           jLbNodeHeight.setEnabled(true); | 
|---|
| 377 |           jTfNodeHeight.setEnabled(true); | 
|---|
| 378 |         } | 
|---|
| 379 |         else { | 
|---|
| 380 |           jLbNodeWidth.setEnabled(false); | 
|---|
| 381 |           jTfNodeWidth.setEnabled(false); | 
|---|
| 382 |           jLbNodeHeight.setEnabled(false); | 
|---|
| 383 |           jTfNodeHeight.setEnabled(false); | 
|---|
| 384 |           setAppropriateNodeSize(); | 
|---|
| 385 |         } | 
|---|
| 386 |       } | 
|---|
| 387 |     }); | 
|---|
| 388 |      | 
|---|
| 389 |      | 
|---|
| 390 |     jBtLayout  = new JButton("Layout Graph"); | 
|---|
| 391 |     jBtLayout.addActionListener( new ActionListener() { | 
|---|
| 392 |       public void actionPerformed(ActionEvent ae) { | 
|---|
| 393 |         int tmpW, tmpH; | 
|---|
| 394 |          | 
|---|
| 395 |         if(jCbCustomNodeSize.isSelected()) { | 
|---|
| 396 |           try{ tmpW = Integer.parseInt(jTfNodeWidth.getText()); } | 
|---|
| 397 |           catch(NumberFormatException ne) { | 
|---|
| 398 |             JOptionPane.showMessageDialog(GraphVisualizer.this.getParent(), | 
|---|
| 399 |             "Invalid integer entered for node width.", | 
|---|
| 400 |             "Error", | 
|---|
| 401 |             JOptionPane.ERROR_MESSAGE); | 
|---|
| 402 |             tmpW = nodeWidth; | 
|---|
| 403 |             jTfNodeWidth.setText(""+nodeWidth); | 
|---|
| 404 |              | 
|---|
| 405 |           } | 
|---|
| 406 |           try{ tmpH = Integer.parseInt(jTfNodeHeight.getText()); } | 
|---|
| 407 |           catch(NumberFormatException ne) { | 
|---|
| 408 |             JOptionPane.showMessageDialog(GraphVisualizer.this.getParent(), | 
|---|
| 409 |             "Invalid integer entered for node height.", | 
|---|
| 410 |             "Error", | 
|---|
| 411 |             JOptionPane.ERROR_MESSAGE); | 
|---|
| 412 |             tmpH = nodeHeight; | 
|---|
| 413 |             jTfNodeWidth.setText(""+nodeHeight); | 
|---|
| 414 |           } | 
|---|
| 415 |            | 
|---|
| 416 |           if(tmpW!=nodeWidth || tmpH!=nodeHeight) { | 
|---|
| 417 |             nodeWidth = tmpW; paddedNodeWidth = nodeWidth+8; nodeHeight = tmpH; | 
|---|
| 418 |           } | 
|---|
| 419 |         } | 
|---|
| 420 |         JButton bt = (JButton)ae.getSource(); | 
|---|
| 421 |         bt.setEnabled(false); | 
|---|
| 422 |         m_le.setNodeSize(paddedNodeWidth, nodeHeight); | 
|---|
| 423 |         m_le.layoutGraph(); | 
|---|
| 424 |       } | 
|---|
| 425 |     }); | 
|---|
| 426 |      | 
|---|
| 427 |      | 
|---|
| 428 |     GridBagConstraints gbc = new GridBagConstraints(); | 
|---|
| 429 |      | 
|---|
| 430 |     final JPanel p = new JPanel(new GridBagLayout()); | 
|---|
| 431 |     gbc.gridwidth = gbc.REMAINDER; | 
|---|
| 432 |     gbc.anchor = gbc.NORTHWEST; | 
|---|
| 433 |     gbc.fill = gbc.NONE; | 
|---|
| 434 |     p.add( m_le.getControlPanel(), gbc); | 
|---|
| 435 |     gbc.gridwidth = 1; | 
|---|
| 436 |     gbc.insets = new Insets(8,0,0,0); | 
|---|
| 437 |     gbc.anchor = gbc.NORTHWEST; | 
|---|
| 438 |     gbc.gridwidth = gbc.REMAINDER; | 
|---|
| 439 |      | 
|---|
| 440 |     p.add( jCbCustomNodeSize, gbc ); | 
|---|
| 441 |     gbc.insets = new Insets(0,0,0,0); | 
|---|
| 442 |     gbc.gridwidth = gbc.REMAINDER; | 
|---|
| 443 |     Container c = new Container(); | 
|---|
| 444 |     c.setLayout( new GridBagLayout() ); | 
|---|
| 445 |     gbc.gridwidth = gbc.RELATIVE; | 
|---|
| 446 |     c.add(jLbNodeWidth, gbc); | 
|---|
| 447 |     gbc.gridwidth = gbc.REMAINDER; | 
|---|
| 448 |     c.add(jTfNodeWidth, gbc); | 
|---|
| 449 |     gbc.gridwidth = gbc.RELATIVE; | 
|---|
| 450 |     c.add(jLbNodeHeight, gbc); | 
|---|
| 451 |     gbc.gridwidth = gbc.REMAINDER; | 
|---|
| 452 |     c.add(jTfNodeHeight, gbc); | 
|---|
| 453 |     gbc.fill = gbc.HORIZONTAL; | 
|---|
| 454 |     p.add( c, gbc ); | 
|---|
| 455 |      | 
|---|
| 456 |     gbc.anchor = gbc.NORTHWEST; | 
|---|
| 457 |     gbc.insets = new Insets(8,0,0,0); | 
|---|
| 458 |     gbc.fill = gbc.HORIZONTAL; | 
|---|
| 459 |     p.add( jBtLayout, gbc ); | 
|---|
| 460 |     gbc.fill = gbc.NONE; | 
|---|
| 461 |     p.setBorder(BorderFactory.createCompoundBorder( | 
|---|
| 462 |     BorderFactory.createTitledBorder("ExtraControls"), | 
|---|
| 463 |     BorderFactory.createEmptyBorder(4,4,4,4) | 
|---|
| 464 |     ) ); | 
|---|
| 465 |     p.setPreferredSize( new Dimension(0, 0) ); | 
|---|
| 466 |      | 
|---|
| 467 |     final JToolBar jTbTools = new JToolBar(); | 
|---|
| 468 |     jTbTools.setFloatable(false); | 
|---|
| 469 |     jTbTools.setLayout( new GridBagLayout() ); | 
|---|
| 470 |     gbc.anchor = gbc.NORTHWEST; | 
|---|
| 471 |     gbc.gridwidth = gbc.REMAINDER; | 
|---|
| 472 |     gbc.insets = new Insets(0,0,0,0); | 
|---|
| 473 |     jTbTools.add(p,gbc); | 
|---|
| 474 |     gbc.gridwidth = 1; | 
|---|
| 475 |     jTbTools.add(m_jBtSave, gbc); | 
|---|
| 476 |     jTbTools.addSeparator(new Dimension(2,2)); | 
|---|
| 477 |     jTbTools.add(jBtZoomIn, gbc); | 
|---|
| 478 |      | 
|---|
| 479 |     gbc.fill = gbc.VERTICAL; | 
|---|
| 480 |     gbc.weighty = 1; | 
|---|
| 481 |     JPanel p2 = new JPanel(new BorderLayout()); | 
|---|
| 482 |     p2.setPreferredSize( jTfZoom.getPreferredSize() ); | 
|---|
| 483 |     p2.setMinimumSize( jTfZoom.getPreferredSize() ); | 
|---|
| 484 |     p2.add(jTfZoom, BorderLayout.CENTER); | 
|---|
| 485 |     jTbTools.add(p2, gbc); | 
|---|
| 486 |     gbc.weighty =0; | 
|---|
| 487 |     gbc.fill = gbc.NONE; | 
|---|
| 488 |      | 
|---|
| 489 |     jTbTools.add(jBtZoomOut, gbc); | 
|---|
| 490 |     jTbTools.addSeparator(new Dimension(2,2)); | 
|---|
| 491 |     jTbTools.add(jBtExtraControls, gbc); | 
|---|
| 492 |     jTbTools.addSeparator(new Dimension(4,2)); | 
|---|
| 493 |     gbc.weightx = 1; | 
|---|
| 494 |     gbc.fill = gbc.BOTH; | 
|---|
| 495 |     jTbTools.add(m_le.getProgressBar(), gbc); | 
|---|
| 496 |      | 
|---|
| 497 |     jBtExtraControls.addActionListener( new ActionListener() { | 
|---|
| 498 |       public void actionPerformed(ActionEvent ae) { | 
|---|
| 499 |         Dimension d = p.getPreferredSize(); | 
|---|
| 500 |         if(d.width==0 || d.height==0) { | 
|---|
| 501 |           LayoutManager lm = p.getLayout(); | 
|---|
| 502 |           Dimension d2 = lm.preferredLayoutSize(p); | 
|---|
| 503 |           p.setPreferredSize(d2); jTbTools.revalidate(); | 
|---|
| 504 |           /* | 
|---|
| 505 |           // this piece of code adds in an animation | 
|---|
| 506 |           // for popping out the extra controls panel | 
|---|
| 507 |           Thread th = new Thread() { | 
|---|
| 508 |             int h = 0, w = 0; | 
|---|
| 509 |             LayoutManager lm = p.getLayout(); | 
|---|
| 510 |             Dimension d2 = lm.preferredLayoutSize(p); | 
|---|
| 511 |             | 
|---|
| 512 |             int tow = (int)d2.getWidth(), toh = (int)d2.getHeight(); | 
|---|
| 513 |             //toh = (int)d2.getHeight(); | 
|---|
| 514 |             //tow = (int)d2.getWidth(); | 
|---|
| 515 |             | 
|---|
| 516 |             public void run() { | 
|---|
| 517 |               while(h<toh || w<tow) { | 
|---|
| 518 |                 if((h+10)<toh) | 
|---|
| 519 |                   h += 10; | 
|---|
| 520 |                 else if(h<toh) | 
|---|
| 521 |                   h = toh; | 
|---|
| 522 |                 if((w+10)<tow) | 
|---|
| 523 |                   w += 10; | 
|---|
| 524 |                 else if(w<tow) | 
|---|
| 525 |                   w = tow; | 
|---|
| 526 |                 p.setPreferredSize(new Dimension(w, h)); | 
|---|
| 527 |                 //p.invalidate(); | 
|---|
| 528 |                 jTbTools.revalidate(); | 
|---|
| 529 |                 //paint(Temp4.this.getGraphics()); | 
|---|
| 530 |                 try {this.sleep(30);} | 
|---|
| 531 |                 catch(InterruptedException ie) {ie.printStackTrace(); break;} | 
|---|
| 532 |               } | 
|---|
| 533 |               p.setPreferredSize(new Dimension(tow,toh)); jTbTools.revalidate(); | 
|---|
| 534 |             } | 
|---|
| 535 |           }; | 
|---|
| 536 |           th.start(); | 
|---|
| 537 |            */ | 
|---|
| 538 |         } | 
|---|
| 539 |         else { | 
|---|
| 540 |           p.setPreferredSize( new Dimension(0,0) ); | 
|---|
| 541 |           jTbTools.revalidate(); | 
|---|
| 542 |           /* | 
|---|
| 543 |           Thread th = new Thread() { | 
|---|
| 544 |             int h = p.getHeight(), w = p.getWidth(); | 
|---|
| 545 |             LayoutManager lm = p.getLayout(); | 
|---|
| 546 |             int tow = 0, toh = 0; | 
|---|
| 547 |             | 
|---|
| 548 |             public void run() { | 
|---|
| 549 |               while(h>toh || w>tow) { | 
|---|
| 550 |                 if((h-10)>toh) | 
|---|
| 551 |                   h -= 10; | 
|---|
| 552 |                 else if(h>toh) | 
|---|
| 553 |                   h = toh; | 
|---|
| 554 |                 if((w-10)>tow) | 
|---|
| 555 |                   w -= 10; | 
|---|
| 556 |                 else if(w>tow) | 
|---|
| 557 |                   w = tow; | 
|---|
| 558 |             | 
|---|
| 559 |                 p.setPreferredSize(new Dimension(w, h)); | 
|---|
| 560 |                 //p.invalidate(); | 
|---|
| 561 |                 jTbTools.revalidate(); | 
|---|
| 562 |                 //paint(Temp4.this.getGraphics()); | 
|---|
| 563 |                 try {this.sleep(30);} | 
|---|
| 564 |                 catch(InterruptedException ie) {ie.printStackTrace(); break;} | 
|---|
| 565 |               } | 
|---|
| 566 |               p.setPreferredSize(new Dimension(tow,toh)); jTbTools.revalidate(); | 
|---|
| 567 |             } | 
|---|
| 568 |           }; | 
|---|
| 569 |           th.start(); | 
|---|
| 570 |            */ | 
|---|
| 571 |         } | 
|---|
| 572 |       } | 
|---|
| 573 |     }); | 
|---|
| 574 |     this.setLayout( new BorderLayout() ); | 
|---|
| 575 |     this.add(jTbTools, BorderLayout.NORTH); | 
|---|
| 576 |     this.add(m_js, BorderLayout.CENTER); | 
|---|
| 577 |   } | 
|---|
| 578 |    | 
|---|
| 579 |    | 
|---|
| 580 |   /** | 
|---|
| 581 |    * This method sets the node size that is appropriate | 
|---|
| 582 |    * considering the maximum label size that is present. | 
|---|
| 583 |    * It is used internally when custom node size checkbox | 
|---|
| 584 |    * is unchecked. | 
|---|
| 585 |    */ | 
|---|
| 586 |   protected void setAppropriateNodeSize() { | 
|---|
| 587 |     int strWidth; | 
|---|
| 588 |     if(maxStringWidth==0) | 
|---|
| 589 |       for(int i=0; i<m_nodes.size(); i++) { | 
|---|
| 590 |         strWidth = fm.stringWidth(((GraphNode)m_nodes.elementAt(i)).lbl); | 
|---|
| 591 |         if(strWidth>maxStringWidth) | 
|---|
| 592 |           maxStringWidth=strWidth; | 
|---|
| 593 |       } | 
|---|
| 594 |     nodeWidth = maxStringWidth+4; | 
|---|
| 595 |     paddedNodeWidth = nodeWidth+8; | 
|---|
| 596 |     jTfNodeWidth.setText(""+nodeWidth); | 
|---|
| 597 |      | 
|---|
| 598 |     nodeHeight = 2*fm.getHeight(); | 
|---|
| 599 |     jTfNodeHeight.setText(""+nodeHeight); | 
|---|
| 600 |   } | 
|---|
| 601 |    | 
|---|
| 602 |   /** | 
|---|
| 603 |    * Sets the preferred size for m_gp GraphPanel to the | 
|---|
| 604 |    * minimum size that is neccessary to display the graph. | 
|---|
| 605 |    */ | 
|---|
| 606 |   protected void setAppropriateSize() { | 
|---|
| 607 |     int maxX=0, maxY=0; | 
|---|
| 608 |      | 
|---|
| 609 |     m_gp.setScale(scale, scale); | 
|---|
| 610 |      | 
|---|
| 611 |     for(int i=0; i<m_nodes.size(); i++) { | 
|---|
| 612 |       GraphNode n = (GraphNode)m_nodes.elementAt(i); | 
|---|
| 613 |       if(maxX<n.x) | 
|---|
| 614 |         maxX=n.x; | 
|---|
| 615 |       if(maxY<n.y) | 
|---|
| 616 |         maxY=n.y; | 
|---|
| 617 |     } | 
|---|
| 618 |     //System.out.println("Scale: "+scale+" paddedWidth: "+paddedNodeWidth+ | 
|---|
| 619 |     //                   " nodeHeight: "+nodeHeight+"\nmaxX: "+maxX+" maxY: "+ | 
|---|
| 620 |     //                   maxY+" final: "+(int)((maxX+paddedNodeWidth+2)*scale)+ | 
|---|
| 621 |     //                   ","+(int)((maxY+nodeHeight+2)*scale) ); | 
|---|
| 622 |     m_gp.setPreferredSize(new Dimension((int)((maxX+paddedNodeWidth+2)*scale), | 
|---|
| 623 |     (int)((maxY+nodeHeight+2)*scale))); | 
|---|
| 624 |     //System.out.println("Size set to "+this.getPreferredSize()); | 
|---|
| 625 |   } | 
|---|
| 626 |    | 
|---|
| 627 |    | 
|---|
| 628 |   /** | 
|---|
| 629 |    * This method is an implementation for LayoutCompleteEventListener | 
|---|
| 630 |    * class. It sets the size appropriate for m_gp GraphPanel and | 
|---|
| 631 |    * and revalidates it's container JScrollPane once a | 
|---|
| 632 |    * LayoutCompleteEvent is received from the LayoutEngine. | 
|---|
| 633 |    */ | 
|---|
| 634 |   public void layoutCompleted(LayoutCompleteEvent le) { | 
|---|
| 635 |     setAppropriateSize(); | 
|---|
| 636 |     //m_gp.clearBuffer(); | 
|---|
| 637 |     m_gp.invalidate(); | 
|---|
| 638 |     m_js.revalidate(); | 
|---|
| 639 |     m_gp.repaint(); | 
|---|
| 640 |     jBtLayout.setEnabled(true); | 
|---|
| 641 |   } | 
|---|
| 642 |    | 
|---|
| 643 |    | 
|---|
| 644 |   /** | 
|---|
| 645 |    * This method lays out the graph by calling the | 
|---|
| 646 |    * LayoutEngine's layoutGraph() method. This method | 
|---|
| 647 |    * should be called to display the graph nicely, unless | 
|---|
| 648 |    * the input XMLBIF03 already contains some layout | 
|---|
| 649 |    * information (ie the x,y positions of nodes. | 
|---|
| 650 |    */ | 
|---|
| 651 |   public void layoutGraph() { | 
|---|
| 652 |     if(m_le!=null) | 
|---|
| 653 |       m_le.layoutGraph(); | 
|---|
| 654 |      | 
|---|
| 655 |   } | 
|---|
| 656 |    | 
|---|
| 657 |   /********************************************************* | 
|---|
| 658 |    * | 
|---|
| 659 |    *  BIF reader<br> | 
|---|
| 660 |    *  Reads a graph description in XMLBIF03 from a string | 
|---|
| 661 |    * | 
|---|
| 662 |    ********************************************************* | 
|---|
| 663 |    */ | 
|---|
| 664 |   public void readBIF(String instring) throws BIFFormatException { | 
|---|
| 665 |     BIFParser bp = new BIFParser(instring, m_nodes, m_edges); | 
|---|
| 666 |     try { | 
|---|
| 667 |       graphID = bp.parse(); | 
|---|
| 668 |     } catch(BIFFormatException bf) { | 
|---|
| 669 |       System.out.println("BIF format error"); | 
|---|
| 670 |       bf.printStackTrace(); | 
|---|
| 671 |     } | 
|---|
| 672 |     catch(Exception ex) { ex.printStackTrace(); return; } | 
|---|
| 673 |      | 
|---|
| 674 |     setAppropriateNodeSize(); | 
|---|
| 675 |     if(m_le!=null) { | 
|---|
| 676 |       m_le.setNodeSize(paddedNodeWidth, nodeHeight); | 
|---|
| 677 |     } | 
|---|
| 678 |   } //end readBIF1 | 
|---|
| 679 |    | 
|---|
| 680 |   /** | 
|---|
| 681 |    * | 
|---|
| 682 |    *  BIF reader<br> | 
|---|
| 683 |    *  Reads a graph description in XMLBIF03 from an InputStrem | 
|---|
| 684 |    * | 
|---|
| 685 |    * | 
|---|
| 686 |    */ | 
|---|
| 687 |   public void readBIF(InputStream instream) throws BIFFormatException { | 
|---|
| 688 |     BIFParser bp = new BIFParser(instream, m_nodes, m_edges); | 
|---|
| 689 |     try { | 
|---|
| 690 |       graphID = bp.parse(); | 
|---|
| 691 |     } catch(BIFFormatException bf) { | 
|---|
| 692 |       System.out.println("BIF format error"); | 
|---|
| 693 |       bf.printStackTrace(); | 
|---|
| 694 |     } | 
|---|
| 695 |     catch(Exception ex) { ex.printStackTrace(); return; } | 
|---|
| 696 |      | 
|---|
| 697 |     setAppropriateNodeSize(); | 
|---|
| 698 |     if(m_le!=null) { | 
|---|
| 699 |       m_le.setNodeSize(paddedNodeWidth, nodeHeight); | 
|---|
| 700 |     } | 
|---|
| 701 |     setAppropriateSize(); | 
|---|
| 702 |   } //end readBIF2 | 
|---|
| 703 |    | 
|---|
| 704 |    | 
|---|
| 705 |   /********************************************************* | 
|---|
| 706 |    * | 
|---|
| 707 |    *  Dot reader<br> | 
|---|
| 708 |    *  Reads a graph description in DOT format from a string | 
|---|
| 709 |    * | 
|---|
| 710 |    ********************************************************* | 
|---|
| 711 |    */ | 
|---|
| 712 |   public void readDOT(Reader input) { | 
|---|
| 713 |     DotParser dp = new DotParser(input, m_nodes, m_edges); | 
|---|
| 714 |     graphID = dp.parse(); | 
|---|
| 715 |      | 
|---|
| 716 |     setAppropriateNodeSize(); | 
|---|
| 717 |     if(m_le!=null) { | 
|---|
| 718 |       m_le.setNodeSize(paddedNodeWidth, nodeHeight); | 
|---|
| 719 |       jBtLayout.setEnabled(false); | 
|---|
| 720 |       layoutGraph(); | 
|---|
| 721 |     } | 
|---|
| 722 |   } | 
|---|
| 723 |    | 
|---|
| 724 |   /** | 
|---|
| 725 |    * The panel which contains the actual graph. | 
|---|
| 726 |    */ | 
|---|
| 727 |   private class GraphPanel | 
|---|
| 728 |     extends PrintablePanel { | 
|---|
| 729 |  | 
|---|
| 730 |     /** for serialization */ | 
|---|
| 731 |     private static final long serialVersionUID = -3562813603236753173L; | 
|---|
| 732 |  | 
|---|
| 733 |     public GraphPanel() { | 
|---|
| 734 |       super(); | 
|---|
| 735 |       this.addMouseListener( new GraphVisualizerMouseListener() ); | 
|---|
| 736 |       this.addMouseMotionListener( new GraphVisualizerMouseMotionListener() ); | 
|---|
| 737 |       this.setToolTipText(""); | 
|---|
| 738 |     } | 
|---|
| 739 |      | 
|---|
| 740 |     public String getToolTipText(MouseEvent me) { | 
|---|
| 741 |       int x, y, nx, ny; | 
|---|
| 742 |       Rectangle r; | 
|---|
| 743 |       GraphNode n; | 
|---|
| 744 |       Dimension d = m_gp.getPreferredSize(); | 
|---|
| 745 |       //System.out.println("Preferred Size: "+this.getPreferredSize()+ | 
|---|
| 746 |       //                   " Actual Size: "+this.getSize()); | 
|---|
| 747 |       x=y=nx=ny=0; | 
|---|
| 748 |        | 
|---|
| 749 |       if(d.width < m_gp.getWidth()) | 
|---|
| 750 |         nx = (int)((nx + m_gp.getWidth()/2 - d.width/2)/scale); | 
|---|
| 751 |       if(d.height < m_gp.getHeight()) | 
|---|
| 752 |         ny = (int)((ny + m_gp.getHeight()/2 - d.height/2)/scale); | 
|---|
| 753 |        | 
|---|
| 754 |       r = new Rectangle(0, 0,  | 
|---|
| 755 |                        (int)(paddedNodeWidth*scale), (int)(nodeHeight*scale)); | 
|---|
| 756 |       x += me.getX(); y += me.getY(); | 
|---|
| 757 |        | 
|---|
| 758 |       int i; | 
|---|
| 759 |       for(i=0; i<m_nodes.size(); i++) { | 
|---|
| 760 |         n = (GraphNode) m_nodes.elementAt(i); | 
|---|
| 761 |         if(n.nodeType!=NORMAL) | 
|---|
| 762 |           return null; | 
|---|
| 763 |         r.x = (int)((nx+n.x)*scale); r.y = (int)((ny+n.y)*scale); | 
|---|
| 764 |         if(r.contains(x,y)) { | 
|---|
| 765 |           if(n.probs==null) | 
|---|
| 766 |             return n.lbl; | 
|---|
| 767 |           else | 
|---|
| 768 |             return n.lbl+" (click to view the probability dist. table)"; | 
|---|
| 769 |         } | 
|---|
| 770 |       } | 
|---|
| 771 |       return null; | 
|---|
| 772 |     } | 
|---|
| 773 |      | 
|---|
| 774 |      | 
|---|
| 775 |     public void paintComponent(Graphics gr) { | 
|---|
| 776 |       Graphics2D g = (Graphics2D)gr; | 
|---|
| 777 |       RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, | 
|---|
| 778 |       RenderingHints.VALUE_ANTIALIAS_ON); | 
|---|
| 779 |       rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); | 
|---|
| 780 |       g.setRenderingHints(rh); | 
|---|
| 781 |       g.scale(scale, scale); | 
|---|
| 782 |       Rectangle r = g.getClipBounds(); | 
|---|
| 783 |       g.clearRect(r.x,r.y,r.width,r.height); | 
|---|
| 784 |       //g.setColor(this.getBackground()); | 
|---|
| 785 |       //g.fillRect(0, 0, width+5, height+5); | 
|---|
| 786 |       int x=0, y=0; | 
|---|
| 787 |       Dimension d = this.getPreferredSize(); | 
|---|
| 788 |       //System.out.println("Preferred Size: "+this.getPreferredSize()+ | 
|---|
| 789 |       //                   " Actual Size: "+this.getSize()); | 
|---|
| 790 |        | 
|---|
| 791 |       //initializing x & y to display the graph in the middle | 
|---|
| 792 |       //if the display area is larger than the graph | 
|---|
| 793 |       if(d.width < this.getWidth()) | 
|---|
| 794 |         x = (int)((x + this.getWidth()/2 - d.width/2)/scale); | 
|---|
| 795 |       if(d.height < this.getHeight()) | 
|---|
| 796 |         y = (int)((y + this.getHeight()/2 - d.height/2)/scale); | 
|---|
| 797 |        | 
|---|
| 798 |       for(int index=0; index<m_nodes.size(); index++) { | 
|---|
| 799 |         GraphNode n = (GraphNode) m_nodes.elementAt(index); | 
|---|
| 800 |         if( n.nodeType==NORMAL) { | 
|---|
| 801 |           g.setColor( this.getBackground().darker().darker() ); | 
|---|
| 802 |           g.fillOval(x+n.x+paddedNodeWidth-nodeWidth- | 
|---|
| 803 |                           (paddedNodeWidth-nodeWidth)/2, | 
|---|
| 804 |                      y+n.y, | 
|---|
| 805 |                      nodeWidth, nodeHeight); | 
|---|
| 806 |            | 
|---|
| 807 |           g.setColor(Color.white); | 
|---|
| 808 |           //g.setColor(Color.black); | 
|---|
| 809 |           //System.out.println("drawing "+ | 
|---|
| 810 |           //                   ((GraphNode)m_nodes.elementAt(index)).ID+ | 
|---|
| 811 |           //                   " at "+" x: "+ (x+n.x+paddedNodeWidth/2- | 
|---|
| 812 |           //      fm.stringWidth( ((GraphNode)m_nodes.elementAt(index)).ID )/2)+ | 
|---|
| 813 |           //                   " y: "+(y+n.y+nodeHeight/2+fm.getHeight()/2-2) ); | 
|---|
| 814 |            | 
|---|
| 815 |            | 
|---|
| 816 |           //Draw the node's label if it can fit inside the node's current | 
|---|
| 817 |           // width otherwise display its ID or otherwise just display its | 
|---|
| 818 |           // idx in the FastVector (to distinguish it from others) | 
|---|
| 819 |           // if any can fit in node's current width | 
|---|
| 820 |           if(fm.stringWidth(n.lbl)<=nodeWidth) | 
|---|
| 821 |             g.drawString( n.lbl, | 
|---|
| 822 |             x+n.x+paddedNodeWidth/2 | 
|---|
| 823 |             -fm.stringWidth( n.lbl )/2, | 
|---|
| 824 |             y+n.y+nodeHeight/2+fm.getHeight()/2-2 ); | 
|---|
| 825 |           else if(fm.stringWidth(n.ID)<=nodeWidth) | 
|---|
| 826 |             g.drawString( n.ID, | 
|---|
| 827 |             x+n.x+paddedNodeWidth/2 | 
|---|
| 828 |             -fm.stringWidth( n.ID )/2, | 
|---|
| 829 |             y+n.y+nodeHeight/2+fm.getHeight()/2-2 ); | 
|---|
| 830 |           else if(fm.stringWidth( Integer.toString(index) )<=nodeWidth) | 
|---|
| 831 |             g.drawString( Integer.toString(index), | 
|---|
| 832 |             x+n.x+paddedNodeWidth/2 | 
|---|
| 833 |             -fm.stringWidth( Integer.toString(index) )/2, | 
|---|
| 834 |             y+n.y+nodeHeight/2+fm.getHeight()/2-2 ); | 
|---|
| 835 |            | 
|---|
| 836 |           g.setColor(Color.black); | 
|---|
| 837 |         } | 
|---|
| 838 |         else { | 
|---|
| 839 |           //g.draw( new java.awt.geom.QuadCurve2D.Double(n.x+paddedNodeWidth/2,  | 
|---|
| 840 |           //                                             n.y, | 
|---|
| 841 |           //                            n.x+paddedNodeWidth-nodeSize | 
|---|
| 842 |           //                                   -(paddedNodeWidth-nodeSize)/2, | 
|---|
| 843 |           //                                  n.y+nodeHeight/2, | 
|---|
| 844 |           //                           n.x+paddedNodeWidth/2, n.y+nodeHeight) ); | 
|---|
| 845 |           g.drawLine(x+n.x+paddedNodeWidth/2, y+n.y,  | 
|---|
| 846 |                      x+n.x+paddedNodeWidth/2, y+n.y+nodeHeight); | 
|---|
| 847 |            | 
|---|
| 848 |         } | 
|---|
| 849 |          | 
|---|
| 850 |         GraphNode n2; | 
|---|
| 851 |         int x1, y1, x2, y2; | 
|---|
| 852 |         //System.out.println("Drawing edges of "+n.lbl); | 
|---|
| 853 |          | 
|---|
| 854 |         //Drawing all the edges coming out from the node, | 
|---|
| 855 |         //including reversed and double ones | 
|---|
| 856 |         if(n.edges!=null) | 
|---|
| 857 |           for(int k=0; k<n.edges.length; k++) { | 
|---|
| 858 |             if(n.edges[k][1]>0) { | 
|---|
| 859 |               n2 = (GraphNode) m_nodes.elementAt(n.edges[k][0]); //m_nodes.elementAt(k); | 
|---|
| 860 |               //System.out.println("  -->to "+n2.lbl); | 
|---|
| 861 |               x1=n.x+paddedNodeWidth/2; y1=n.y+nodeHeight; | 
|---|
| 862 |               x2=n2.x+paddedNodeWidth/2; y2=n2.y; | 
|---|
| 863 |               g.drawLine(x+x1, y+y1, x+x2, y+y2); | 
|---|
| 864 |               if(n.edges[k][1]==DIRECTED) { | 
|---|
| 865 |                 if(n2.nodeType==n2.NORMAL) | 
|---|
| 866 |                   drawArrow(g, x+x1, y+y1, x+x2, y+y2); | 
|---|
| 867 |               } | 
|---|
| 868 |               else if(n.edges[k][1]==REVERSED) { | 
|---|
| 869 |                 if(n.nodeType==NORMAL) | 
|---|
| 870 |                   drawArrow(g, x+x2, y+y2, x+x1, y+y1); | 
|---|
| 871 |               } | 
|---|
| 872 |               else if(n.edges[k][1]==DOUBLE) { | 
|---|
| 873 |                 if(n.nodeType==NORMAL) | 
|---|
| 874 |                   drawArrow(g, x+x2, y+y2, x+x1, y+y1); | 
|---|
| 875 |                 if(n2.nodeType==NORMAL) | 
|---|
| 876 |                   drawArrow(g, x+x1, y+y1, x+x2, y+y2); | 
|---|
| 877 |               } | 
|---|
| 878 |             } | 
|---|
| 879 |           } | 
|---|
| 880 |       } | 
|---|
| 881 |     } | 
|---|
| 882 |      | 
|---|
| 883 |     /** | 
|---|
| 884 |      * This method draws an arrow on a line from (x1,y1) | 
|---|
| 885 |      * to (x2,y2). The arrow head is seated on (x2,y2) and | 
|---|
| 886 |      * is in the direction of the line. | 
|---|
| 887 |      * If the arrow is needed to be drawn in the opposite | 
|---|
| 888 |      * direction then simply swap the order of (x1, y1) | 
|---|
| 889 |      * and (x2, y2) when calling this function. | 
|---|
| 890 |      */ | 
|---|
| 891 |     protected void drawArrow(Graphics g, int x1, int y1, int x2, int y2) { | 
|---|
| 892 |        | 
|---|
| 893 |       if(x1==x2) { | 
|---|
| 894 |         if(y1<y2) { | 
|---|
| 895 |           g.drawLine(x2, y2, x2+4, y2-8); | 
|---|
| 896 |           g.drawLine(x2, y2, x2-4, y2-8); | 
|---|
| 897 |         } | 
|---|
| 898 |         else { | 
|---|
| 899 |           g.drawLine(x2, y2, x2+4, y2+8); | 
|---|
| 900 |           g.drawLine(x2, y2, x2-4, y2+8); | 
|---|
| 901 |         } | 
|---|
| 902 |       } | 
|---|
| 903 |       else { | 
|---|
| 904 |         //theta=line's angle from base, beta=angle of arrow's side from line | 
|---|
| 905 |         double hyp=0, base=0, perp=0, theta, beta; | 
|---|
| 906 |         int x3=0, y3=0; | 
|---|
| 907 |          | 
|---|
| 908 |         if(x2<x1) { | 
|---|
| 909 |           base = x1-x2; hyp = Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) ); | 
|---|
| 910 |           theta = Math.acos( base/hyp ); | 
|---|
| 911 |         } | 
|---|
| 912 |         else { //x1>x2 as we already checked x1==x2 before | 
|---|
| 913 |           base = x1-x2; hyp = Math.sqrt( (x2-x1)*(x2-x1) + (y2-y1)*(y2-y1) ); | 
|---|
| 914 |           theta = Math.acos( base/hyp ); | 
|---|
| 915 |         } | 
|---|
| 916 |         beta = 30*Math.PI/180; | 
|---|
| 917 |         //System.out.println("Original base "+base+" perp "+perp+" hyp "+hyp+ | 
|---|
| 918 |         //                   "\ntheta "+theta+" beta "+beta); | 
|---|
| 919 |          | 
|---|
| 920 |         hyp = 8; | 
|---|
| 921 |         base = Math.cos(theta-beta)*hyp; | 
|---|
| 922 |         perp = Math.sin(theta-beta)*hyp; | 
|---|
| 923 |          | 
|---|
| 924 |         x3 = (int)(x2+base); | 
|---|
| 925 |         if(y1<y2) | 
|---|
| 926 |           y3 = (int)(y2-perp); | 
|---|
| 927 |         else | 
|---|
| 928 |           y3 = (int)(y2+perp); | 
|---|
| 929 |          | 
|---|
| 930 |         //System.out.println("Drawing 1 from "+x2+","+y2+" to "+x3+","+y3+ | 
|---|
| 931 |         //                   " x1,y1 is "+x1+","+y1+" base "+base+ | 
|---|
| 932 |         //                   " perp "+perp+" cos(theta-beta) "+ | 
|---|
| 933 |         //                   Math.cos(theta-beta)); | 
|---|
| 934 |         g.drawLine(x2, y2, x3, y3); | 
|---|
| 935 |          | 
|---|
| 936 |         base = Math.cos(theta+beta)*hyp; | 
|---|
| 937 |         perp = Math.sin(theta+beta)*hyp; | 
|---|
| 938 |          | 
|---|
| 939 |         x3 = (int)(x2+base); | 
|---|
| 940 |         if(y1<y2) | 
|---|
| 941 |           y3 = (int)(y2-perp); | 
|---|
| 942 |         else | 
|---|
| 943 |           y3 = (int)(y2+perp); | 
|---|
| 944 |         //System.out.println("Drawing 2 from "+x2+","+y2+" to "+x3+","+y3+ | 
|---|
| 945 |         //                   " x1,y1 is "+x1+","+y1+" base "+base+ | 
|---|
| 946 |         //                   " perp "+perp); | 
|---|
| 947 |         g.drawLine(x2, y2, x3, y3); | 
|---|
| 948 |       } | 
|---|
| 949 |     } | 
|---|
| 950 |      | 
|---|
| 951 |     /** | 
|---|
| 952 |      * This method highlights a given node and all its children | 
|---|
| 953 |      * and the edges coming out of it. | 
|---|
| 954 |      */ | 
|---|
| 955 |     public void highLight(GraphNode n) { | 
|---|
| 956 |       Graphics2D g = (Graphics2D) this.getGraphics(); | 
|---|
| 957 |       RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, | 
|---|
| 958 |       RenderingHints.VALUE_ANTIALIAS_ON); | 
|---|
| 959 |       rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); | 
|---|
| 960 |       g.setRenderingHints(rh); | 
|---|
| 961 |       g.setPaintMode(); | 
|---|
| 962 |       g.scale(scale, scale); | 
|---|
| 963 |       int x=0, y=0; | 
|---|
| 964 |       Dimension d = this.getPreferredSize(); | 
|---|
| 965 |       //System.out.println("Preferred Size: "+this.getPreferredSize()+ | 
|---|
| 966 |       //                   " Actual Size: "+this.getSize()); | 
|---|
| 967 |        | 
|---|
| 968 |       //initializing x & y to display the graph in the middle | 
|---|
| 969 |       //if the display area is larger than the graph | 
|---|
| 970 |       if(d.width < this.getWidth()) | 
|---|
| 971 |         x = (int)((x + this.getWidth()/2 - d.width/2)/scale); | 
|---|
| 972 |       if(d.height < this.getHeight()) | 
|---|
| 973 |         y = (int)((y + this.getHeight()/2 - d.height/2)/scale); | 
|---|
| 974 |        | 
|---|
| 975 |       //if the node is of type NORMAL only then highlight | 
|---|
| 976 |       if(n.nodeType==NORMAL) { | 
|---|
| 977 |          | 
|---|
| 978 |         g.setXORMode(Color.green); //g.setColor(Color.green); | 
|---|
| 979 |          | 
|---|
| 980 |         g.fillOval(x+n.x+paddedNodeWidth-nodeWidth- | 
|---|
| 981 |         (paddedNodeWidth-nodeWidth)/2, | 
|---|
| 982 |         y+n.y, nodeWidth, nodeHeight); | 
|---|
| 983 |         g.setXORMode(Color.red); | 
|---|
| 984 |          | 
|---|
| 985 |         //Draw the node's label if it can fit inside the node's current | 
|---|
| 986 |         // width otherwise display its ID or otherwise just display its | 
|---|
| 987 |         // idx in the FastVector (to distinguish it from others) | 
|---|
| 988 |         // if any can fit in node's current width | 
|---|
| 989 |         if(fm.stringWidth(n.lbl)<=nodeWidth) | 
|---|
| 990 |           g.drawString( n.lbl, | 
|---|
| 991 |           x+n.x+paddedNodeWidth/2 | 
|---|
| 992 |           -fm.stringWidth( n.lbl )/2, | 
|---|
| 993 |           y+n.y+nodeHeight/2+fm.getHeight()/2-2 ); | 
|---|
| 994 |         else if(fm.stringWidth(n.ID)<=nodeWidth) | 
|---|
| 995 |           g.drawString( n.ID, | 
|---|
| 996 |           x+n.x+paddedNodeWidth/2 | 
|---|
| 997 |           -fm.stringWidth( n.ID )/2, | 
|---|
| 998 |           y+n.y+nodeHeight/2+fm.getHeight()/2-2 ); | 
|---|
| 999 |         else if( fm.stringWidth( Integer.toString(m_nodes.indexOf(n)) ) <= | 
|---|
| 1000 |         nodeWidth ) | 
|---|
| 1001 |           g.drawString( Integer.toString(m_nodes.indexOf(n)), | 
|---|
| 1002 |           x+n.x+paddedNodeWidth/2 | 
|---|
| 1003 |           -fm.stringWidth( Integer.toString(m_nodes.indexOf(n)) )/2, | 
|---|
| 1004 |           y+n.y+nodeHeight/2+fm.getHeight()/2-2 ); | 
|---|
| 1005 |          | 
|---|
| 1006 |         g.setXORMode(Color.green); | 
|---|
| 1007 |          | 
|---|
| 1008 |          | 
|---|
| 1009 |         GraphNode n2; | 
|---|
| 1010 |         int x1, y1, x2, y2; | 
|---|
| 1011 |         //System.out.println("Drawing edges of "+n.lbl); | 
|---|
| 1012 |         if(n.edges!=null) | 
|---|
| 1013 |           //Drawing all the edges from and upward ones coming to the node | 
|---|
| 1014 |           for(int k=0; k<n.edges.length; k++) { | 
|---|
| 1015 |             if(n.edges[k][1]==DIRECTED || n.edges[k][1]==DOUBLE) { | 
|---|
| 1016 |               n2 = (GraphNode) m_nodes.elementAt(n.edges[k][0]); //m_nodes.elementAt(k); | 
|---|
| 1017 |               //System.out.println("  -->to "+n2.lbl); | 
|---|
| 1018 |               x1=n.x+paddedNodeWidth/2; y1=n.y+nodeHeight; | 
|---|
| 1019 |               x2=n2.x+paddedNodeWidth/2; y2=n2.y; | 
|---|
| 1020 |               g.drawLine(x+x1, y+y1, x+x2, y+y2); | 
|---|
| 1021 |               if(n.edges[k][1]==DIRECTED) { | 
|---|
| 1022 |                 if(n2.nodeType==n2.NORMAL) //!n2.dummy) | 
|---|
| 1023 |                   drawArrow(g, x+x1, y+y1, x+x2, y+y2); | 
|---|
| 1024 |               } | 
|---|
| 1025 |               else if(n.edges[k][1]==DOUBLE) { | 
|---|
| 1026 |                 if(n.nodeType==NORMAL) //!n.dummy) | 
|---|
| 1027 |                   drawArrow(g, x+x2, y+y2, x+x1, y+y1); | 
|---|
| 1028 |                 if(n2.nodeType==NORMAL) //!n2.dummy) | 
|---|
| 1029 |                   drawArrow(g, x+x1, y+y1, x+x2, y+y2); | 
|---|
| 1030 |               } | 
|---|
| 1031 |               if(n2.nodeType==NORMAL) | 
|---|
| 1032 |                 g.fillOval(x+n2.x+paddedNodeWidth-nodeWidth- | 
|---|
| 1033 |                 (paddedNodeWidth-nodeWidth)/2, | 
|---|
| 1034 |                 y+n2.y, nodeWidth, nodeHeight); | 
|---|
| 1035 |                | 
|---|
| 1036 |               //If n2 is not of NORMAL type | 
|---|
| 1037 |               // then carry on drawing all the edges and add all the | 
|---|
| 1038 |               // dummy nodes encountered in a Vector until no | 
|---|
| 1039 |               // more dummy nodes are found and all the child nodes(node n2) | 
|---|
| 1040 |               // are of type normal | 
|---|
| 1041 |               java.util.Vector t = new java.util.Vector(); | 
|---|
| 1042 |               while(n2.nodeType!=NORMAL || t.size()>0) { //n2.dummy==true) { | 
|---|
| 1043 |                 //System.out.println("in while processing "+n2.ID); | 
|---|
| 1044 |                 if(t.size()>0) | 
|---|
| 1045 |                 { n2 = (GraphNode)t.elementAt(0); | 
|---|
| 1046 |                   t.removeElementAt(0); } | 
|---|
| 1047 |                 if(n2.nodeType!=NORMAL) { | 
|---|
| 1048 |                   g.drawLine(x+n2.x+paddedNodeWidth/2, y+n2.y, | 
|---|
| 1049 |                   x+n2.x+paddedNodeWidth/2, y+n2.y+nodeHeight); | 
|---|
| 1050 |                   x1=n2.x+paddedNodeWidth/2; y1=n2.y+nodeHeight; | 
|---|
| 1051 |                   //System.out.println("Drawing from "+n2.lbl); | 
|---|
| 1052 |                   for(int m=0; m<n2.edges.length; m++) { | 
|---|
| 1053 |                     //System.out.println(" to "+n2.lbl+", "+ | 
|---|
| 1054 |                     //                   graphMatrix[tmpIndex][m]); | 
|---|
| 1055 |                     if(n2.edges[m][1]>0) { | 
|---|
| 1056 |                       GraphNode n3 = | 
|---|
| 1057 |                       (GraphNode) m_nodes.elementAt(n2.edges[m][0]); //m_nodes.elementAt(m); | 
|---|
| 1058 |                       g.drawLine(x+x1, y+y1, x+n3.x+paddedNodeWidth/2, y+n3.y); | 
|---|
| 1059 |                        | 
|---|
| 1060 |                       if(n3.nodeType==NORMAL){ //!n2.dummy) | 
|---|
| 1061 |                         g.fillOval(x+n3.x+paddedNodeWidth-nodeWidth- | 
|---|
| 1062 |                         (paddedNodeWidth-nodeWidth)/2, | 
|---|
| 1063 |                         y+n3.y, nodeWidth, nodeHeight); | 
|---|
| 1064 |                         drawArrow(g, x+x1, y+y1, | 
|---|
| 1065 |                         x+n3.x+paddedNodeWidth/2, y+n3.y); | 
|---|
| 1066 |                       } | 
|---|
| 1067 |                       //if(n3.nodeType!=n3.NORMAL) | 
|---|
| 1068 |                       t.addElement(n3); | 
|---|
| 1069 |                       //break; | 
|---|
| 1070 |                     } | 
|---|
| 1071 |                   } | 
|---|
| 1072 |                 } | 
|---|
| 1073 |               } | 
|---|
| 1074 |             } | 
|---|
| 1075 |             else if(n.edges[k][1]==-REVERSED || n.edges[k][1]==-DOUBLE) { | 
|---|
| 1076 |               //Drawing all the reversed and double edges which are going | 
|---|
| 1077 |               //upwards in the drawing. | 
|---|
| 1078 |               n2 = (GraphNode) m_nodes.elementAt(n.edges[k][0]); //m_nodes.elementAt(k); | 
|---|
| 1079 |               //System.out.println("  -->to "+n2.lbl); | 
|---|
| 1080 |               x1=n.x+paddedNodeWidth/2; y1=n.y; | 
|---|
| 1081 |               x2=n2.x+paddedNodeWidth/2; y2=n2.y+nodeHeight; | 
|---|
| 1082 |               g.drawLine(x+x1, y+y1, x+x2, y+y2); | 
|---|
| 1083 |                | 
|---|
| 1084 |               if(n.edges[k][1]==-DOUBLE) { | 
|---|
| 1085 |                 drawArrow(g, x+x2, y+y2, x+x1, y+y1); | 
|---|
| 1086 |                 if(n2.nodeType!=SINGULAR_DUMMY) //!n2.dummy) | 
|---|
| 1087 |                   drawArrow(g, x+x1, y+y1, x+x2, y+y2); | 
|---|
| 1088 |               } | 
|---|
| 1089 |                | 
|---|
| 1090 |               int tmpIndex=k; | 
|---|
| 1091 |               while(n2.nodeType!=NORMAL) { //n2.dummy==true) { | 
|---|
| 1092 |                 g.drawLine(x+n2.x+paddedNodeWidth/2, | 
|---|
| 1093 |                 y+n2.y+nodeHeight, x+n2.x+paddedNodeWidth/2, y+n2.y); | 
|---|
| 1094 |                 x1=n2.x+paddedNodeWidth/2; y1=n2.y; | 
|---|
| 1095 |                 for(int m=0; m<n2.edges.length; m++) { | 
|---|
| 1096 |                   if(n2.edges[m][1]<0) { | 
|---|
| 1097 |                     n2 = (GraphNode) m_nodes.elementAt(n2.edges[m][0]); //m_nodes.elementAt(m); | 
|---|
| 1098 |                     g.drawLine(x+x1, y+y1, | 
|---|
| 1099 |                     x+n2.x+paddedNodeWidth/2, y+n2.y+nodeHeight); | 
|---|
| 1100 |                     tmpIndex=m; | 
|---|
| 1101 |                     if(n2.nodeType!=SINGULAR_DUMMY) //!n2.dummy) | 
|---|
| 1102 |                       drawArrow(g, x+x1, y+y1, | 
|---|
| 1103 |                       x+n2.x+paddedNodeWidth/2, y+n2.y+nodeHeight); | 
|---|
| 1104 |                     break; | 
|---|
| 1105 |                   } | 
|---|
| 1106 |                 } | 
|---|
| 1107 |               } | 
|---|
| 1108 |             } | 
|---|
| 1109 |           } | 
|---|
| 1110 |       } | 
|---|
| 1111 |     } | 
|---|
| 1112 |   } | 
|---|
| 1113 |    | 
|---|
| 1114 |    | 
|---|
| 1115 |   /** | 
|---|
| 1116 |    * Table Model for the Table that shows the probability | 
|---|
| 1117 |    * distribution for a node | 
|---|
| 1118 |    */ | 
|---|
| 1119 |   private class GraphVisualizerTableModel | 
|---|
| 1120 |     extends AbstractTableModel { | 
|---|
| 1121 |  | 
|---|
| 1122 |     /** for serialization */ | 
|---|
| 1123 |     private static final long serialVersionUID = -4789813491347366596L; | 
|---|
| 1124 |      | 
|---|
| 1125 |     final String[] columnNames; | 
|---|
| 1126 |     final double[][] data; | 
|---|
| 1127 |      | 
|---|
| 1128 |      | 
|---|
| 1129 |     public GraphVisualizerTableModel(double[][] d, String[] c) { | 
|---|
| 1130 |       data = d; | 
|---|
| 1131 |       columnNames = c; | 
|---|
| 1132 |     } | 
|---|
| 1133 |      | 
|---|
| 1134 |     public int getColumnCount() { | 
|---|
| 1135 |       return columnNames.length; | 
|---|
| 1136 |     } | 
|---|
| 1137 |      | 
|---|
| 1138 |     public int getRowCount() { | 
|---|
| 1139 |       return data.length; | 
|---|
| 1140 |     } | 
|---|
| 1141 |      | 
|---|
| 1142 |     public String getColumnName(int col) { | 
|---|
| 1143 |       return columnNames[col]; | 
|---|
| 1144 |     } | 
|---|
| 1145 |      | 
|---|
| 1146 |     public Object getValueAt(int row, int col) { | 
|---|
| 1147 |       return new Double(data[row][col]); | 
|---|
| 1148 |     } | 
|---|
| 1149 |      | 
|---|
| 1150 |    /* | 
|---|
| 1151 |     * JTable uses this method to determine the default renderer/ | 
|---|
| 1152 |     * editor for each cell. | 
|---|
| 1153 |     */ | 
|---|
| 1154 |     public Class getColumnClass(int c) { | 
|---|
| 1155 |       return getValueAt(0, c).getClass(); | 
|---|
| 1156 |     } | 
|---|
| 1157 |      | 
|---|
| 1158 |     /* | 
|---|
| 1159 |      * Implemented this to make sure the table is uneditable. | 
|---|
| 1160 |      */ | 
|---|
| 1161 |     public boolean isCellEditable(int row, int col) { | 
|---|
| 1162 |       return false; | 
|---|
| 1163 |     } | 
|---|
| 1164 |   } | 
|---|
| 1165 |    | 
|---|
| 1166 |    | 
|---|
| 1167 |    | 
|---|
| 1168 |   /** | 
|---|
| 1169 |    * Listener class for processing mouseClicked | 
|---|
| 1170 |    */ | 
|---|
| 1171 |   private class GraphVisualizerMouseListener extends MouseAdapter { | 
|---|
| 1172 |     int x, y, nx, ny; Rectangle r; | 
|---|
| 1173 |      | 
|---|
| 1174 |     /** | 
|---|
| 1175 |      * If the mouse is clicked on a node then this method | 
|---|
| 1176 |      * displays a dialog box with the probability distribution | 
|---|
| 1177 |      * table for that node IF it exists | 
|---|
| 1178 |      */ | 
|---|
| 1179 |     public void mouseClicked(MouseEvent me) { | 
|---|
| 1180 |       GraphNode n; | 
|---|
| 1181 |       Dimension d = m_gp.getPreferredSize(); | 
|---|
| 1182 |       //System.out.println("Preferred Size: "+this.getPreferredSize()+ | 
|---|
| 1183 |       //                   " Actual Size: "+this.getSize()); | 
|---|
| 1184 |       x=y=nx=ny=0; | 
|---|
| 1185 |        | 
|---|
| 1186 |       if(d.width < m_gp.getWidth()) | 
|---|
| 1187 |         nx = (int)((nx + m_gp.getWidth()/2 - d.width/2)/scale); | 
|---|
| 1188 |       if(d.height < m_gp.getHeight()) | 
|---|
| 1189 |         ny = (int)((ny + m_gp.getHeight()/2 - d.height/2)/scale); | 
|---|
| 1190 |        | 
|---|
| 1191 |       r=new Rectangle(0, 0,  | 
|---|
| 1192 |                      (int)(paddedNodeWidth*scale), (int)(nodeHeight*scale)); | 
|---|
| 1193 |       x += me.getX(); y += me.getY(); | 
|---|
| 1194 |        | 
|---|
| 1195 |       int i; | 
|---|
| 1196 |       for(i=0; i<m_nodes.size(); i++) { | 
|---|
| 1197 |         n = (GraphNode) m_nodes.elementAt(i); | 
|---|
| 1198 |         r.x = (int)((nx+n.x)*scale); r.y = (int)((ny+n.y)*scale); | 
|---|
| 1199 |         if(r.contains(x,y)) { | 
|---|
| 1200 |           if(n.probs==null) | 
|---|
| 1201 |             return; | 
|---|
| 1202 |            | 
|---|
| 1203 |           int noOfPrntsOutcomes = 1; | 
|---|
| 1204 |           if(n.prnts!=null) { | 
|---|
| 1205 |             for(int j=0; j<n.prnts.length; j++) { | 
|---|
| 1206 |               GraphNode n2 = (GraphNode)m_nodes.elementAt(n.prnts[j]); | 
|---|
| 1207 |               noOfPrntsOutcomes *= n2.outcomes.length; | 
|---|
| 1208 |             } | 
|---|
| 1209 |             if(noOfPrntsOutcomes>511) { | 
|---|
| 1210 |               System.err.println("Too many outcomes of parents ("+noOfPrntsOutcomes+ | 
|---|
| 1211 |                                  ") can't display probabilities"); | 
|---|
| 1212 |               return; | 
|---|
| 1213 |             } | 
|---|
| 1214 |           } | 
|---|
| 1215 |            | 
|---|
| 1216 |           GraphVisualizerTableModel tm =  | 
|---|
| 1217 |                              new GraphVisualizerTableModel(n.probs, n.outcomes); | 
|---|
| 1218 |            | 
|---|
| 1219 |           JTable jTblProbs = new JTable(tm); //JTable(probabilities, (Object[])n.outcomes); | 
|---|
| 1220 |            | 
|---|
| 1221 |           JScrollPane js = new JScrollPane(jTblProbs); | 
|---|
| 1222 |            | 
|---|
| 1223 |           if(n.prnts!=null) { | 
|---|
| 1224 |             GridBagConstraints gbc = new GridBagConstraints(); | 
|---|
| 1225 |             JPanel jPlRowHeader = new JPanel( new GridBagLayout() ); | 
|---|
| 1226 |              | 
|---|
| 1227 |             //indices of the parent nodes in the Vector | 
|---|
| 1228 |             int [] idx = new int[n.prnts.length];  | 
|---|
| 1229 |             //max length of values of each parent | 
|---|
| 1230 |             int [] lengths = new int[n.prnts.length];   | 
|---|
| 1231 |              | 
|---|
| 1232 |             //System.out.println("n.probs.length "+n.probs.length+ | 
|---|
| 1233 |             //                   " should be "+noOfPrntsOutcomes); | 
|---|
| 1234 |             //System.out.println("n.probs[0].length "+n.probs[0].length+ | 
|---|
| 1235 |             //                   " should be "+n.outcomes.length); | 
|---|
| 1236 |             //System.out.println("probabilities are: "); | 
|---|
| 1237 |             //for(int j=0; j<probabilities.length; j++) { | 
|---|
| 1238 |             //    for(int k=0; k<probabilities[j].length; k++) | 
|---|
| 1239 |             //     System.out.print(probabilities[j][k]+" "); | 
|---|
| 1240 |             //     System.out.println(""); | 
|---|
| 1241 |             //} | 
|---|
| 1242 |              | 
|---|
| 1243 |             //Adding labels for rows | 
|---|
| 1244 |             gbc.anchor = gbc.NORTHWEST; | 
|---|
| 1245 |             gbc.fill = gbc.HORIZONTAL; | 
|---|
| 1246 |             gbc.insets = new Insets(0,1,0,0); | 
|---|
| 1247 |             int addNum=0, temp=0; | 
|---|
| 1248 |             boolean dark=false; | 
|---|
| 1249 |             while(true){ | 
|---|
| 1250 |               GraphNode n2; | 
|---|
| 1251 |               gbc.gridwidth = 1; | 
|---|
| 1252 |               for(int k=0; k<n.prnts.length; k++) { | 
|---|
| 1253 |                 n2 = (GraphNode)m_nodes.elementAt(n.prnts[k]); | 
|---|
| 1254 |                 JLabel lb = new JLabel(n2.outcomes[idx[k]]); | 
|---|
| 1255 |                 lb.setFont( new Font("Dialog", Font.PLAIN, 12) ); | 
|---|
| 1256 |                 lb.setOpaque( true ); | 
|---|
| 1257 |                 lb.setBorder( BorderFactory.createEmptyBorder( 1,2,1,1 ) ); | 
|---|
| 1258 |                 lb.setHorizontalAlignment( JLabel.CENTER ); | 
|---|
| 1259 |                 if(dark) { | 
|---|
| 1260 |                   lb.setBackground( lb.getBackground().darker() ); | 
|---|
| 1261 |                   lb.setForeground( Color.white ); | 
|---|
| 1262 |                 } | 
|---|
| 1263 |                 else | 
|---|
| 1264 |                   lb.setForeground( Color.black ); | 
|---|
| 1265 |                  | 
|---|
| 1266 |                 temp = lb.getPreferredSize().width; | 
|---|
| 1267 |                 //System.out.println("Preferred width "+temp+ | 
|---|
| 1268 |                 //                   " for "+n2.outcomes[idx[k]]); | 
|---|
| 1269 |                 lb.setPreferredSize( | 
|---|
| 1270 |                                  new Dimension(temp, jTblProbs.getRowHeight()) | 
|---|
| 1271 |                                  ); | 
|---|
| 1272 |                 if(lengths[k]<temp) | 
|---|
| 1273 |                   lengths[k] = temp; | 
|---|
| 1274 |                 temp=0; | 
|---|
| 1275 |                  | 
|---|
| 1276 |                 if(k==n.prnts.length-1) { | 
|---|
| 1277 |                   gbc.gridwidth = gbc.REMAINDER; | 
|---|
| 1278 |                   dark = (dark==true) ?  false:true; | 
|---|
| 1279 |                 } | 
|---|
| 1280 |                 jPlRowHeader.add(lb, gbc); | 
|---|
| 1281 |                 addNum++; | 
|---|
| 1282 |               } | 
|---|
| 1283 |                | 
|---|
| 1284 |               for(int k=n.prnts.length-1; k>=0; k--) { | 
|---|
| 1285 |                 n2 = (GraphNode) m_nodes.elementAt(n.prnts[k]); | 
|---|
| 1286 |                 if(idx[k]==n2.outcomes.length-1 && k!=0) { | 
|---|
| 1287 |                   idx[k]=0; | 
|---|
| 1288 |                   continue; | 
|---|
| 1289 |                 } | 
|---|
| 1290 |                 else { | 
|---|
| 1291 |                   idx[k]++; | 
|---|
| 1292 |                   break; | 
|---|
| 1293 |                 } | 
|---|
| 1294 |               } | 
|---|
| 1295 |                | 
|---|
| 1296 |               n2 = (GraphNode) m_nodes.elementAt(n.prnts[0]); | 
|---|
| 1297 |               if(idx[0]==n2.outcomes.length) { | 
|---|
| 1298 |                 JLabel lb= (JLabel) jPlRowHeader.getComponent(addNum-1); | 
|---|
| 1299 |                 jPlRowHeader.remove(addNum-1); | 
|---|
| 1300 |                 lb.setPreferredSize( new Dimension(lb.getPreferredSize().width,  | 
|---|
| 1301 |                                                    jTblProbs.getRowHeight()) ); | 
|---|
| 1302 |                 gbc.gridwidth = gbc.REMAINDER; | 
|---|
| 1303 |                 gbc.weighty = 1; | 
|---|
| 1304 |                 jPlRowHeader.add(lb, gbc); | 
|---|
| 1305 |                 gbc.weighty=0; | 
|---|
| 1306 |                 break; | 
|---|
| 1307 |               } | 
|---|
| 1308 |             } | 
|---|
| 1309 |              | 
|---|
| 1310 |              | 
|---|
| 1311 |             gbc.gridwidth = 1; | 
|---|
| 1312 |             //The following panel contains the names of the parents | 
|---|
| 1313 |             //and is displayed above the row names to identify | 
|---|
| 1314 |             //which value belongs to which parent | 
|---|
| 1315 |             JPanel jPlRowNames = new JPanel(new GridBagLayout()); | 
|---|
| 1316 |             for(int j=0; j<n.prnts.length; j++) { | 
|---|
| 1317 |               JLabel lb2; | 
|---|
| 1318 |               JLabel lb1 =  | 
|---|
| 1319 |                    new JLabel( ((GraphNode)m_nodes.elementAt(n.prnts[j])).lbl ); | 
|---|
| 1320 |               lb1.setBorder( BorderFactory.createEmptyBorder( 1,2,1,1 ) ); | 
|---|
| 1321 |               Dimension tempd = lb1.getPreferredSize(); | 
|---|
| 1322 |               //System.out.println("lengths[j]: "+lengths[j]+ | 
|---|
| 1323 |               //                   " tempd.width: "+tempd.width); | 
|---|
| 1324 |               if(tempd.width<lengths[j]) { | 
|---|
| 1325 |                 lb1.setPreferredSize( new Dimension(lengths[j], tempd.height) ); | 
|---|
| 1326 |                 lb1.setHorizontalAlignment( JLabel.CENTER ); | 
|---|
| 1327 |                 lb1.setMinimumSize( new Dimension(lengths[j], tempd.height) ); | 
|---|
| 1328 |               } | 
|---|
| 1329 |               else if(tempd.width>lengths[j]) { | 
|---|
| 1330 |                 lb2 = (JLabel) jPlRowHeader.getComponent(j); | 
|---|
| 1331 |                 lb2.setPreferredSize( new Dimension(tempd.width,  | 
|---|
| 1332 |                                                lb2.getPreferredSize().height) ); | 
|---|
| 1333 |               } | 
|---|
| 1334 |               jPlRowNames.add(lb1, gbc); | 
|---|
| 1335 |               //System.out.println("After adding "+lb1.getPreferredSize()); | 
|---|
| 1336 |             } | 
|---|
| 1337 |             js.setRowHeaderView(jPlRowHeader); | 
|---|
| 1338 |             js.setCorner( JScrollPane.UPPER_LEFT_CORNER, jPlRowNames ); | 
|---|
| 1339 |           } | 
|---|
| 1340 |            | 
|---|
| 1341 |            | 
|---|
| 1342 |           JDialog jd =  | 
|---|
| 1343 |                 new JDialog((Frame)GraphVisualizer.this.getTopLevelAncestor(), | 
|---|
| 1344 |                             "Probability Distribution Table For "+n.lbl, true); | 
|---|
| 1345 |           jd.setSize(500, 400); | 
|---|
| 1346 |           jd.setLocation(GraphVisualizer.this.getLocation().x+ | 
|---|
| 1347 |                             GraphVisualizer.this.getWidth()/2-250, | 
|---|
| 1348 |                          GraphVisualizer.this.getLocation().y+ | 
|---|
| 1349 |                             GraphVisualizer.this.getHeight()/2-200 ); | 
|---|
| 1350 |            | 
|---|
| 1351 |           jd.getContentPane().setLayout( new BorderLayout() ); | 
|---|
| 1352 |           jd.getContentPane().add(js, BorderLayout.CENTER); | 
|---|
| 1353 |           jd.setVisible(true); | 
|---|
| 1354 |            | 
|---|
| 1355 |           return; | 
|---|
| 1356 |         } | 
|---|
| 1357 |       } | 
|---|
| 1358 |     } | 
|---|
| 1359 |      | 
|---|
| 1360 |   } | 
|---|
| 1361 |    | 
|---|
| 1362 |    | 
|---|
| 1363 |   /** | 
|---|
| 1364 |    * private class for handling mouseMoved events | 
|---|
| 1365 |    * to highlight nodes if the the mouse is moved on | 
|---|
| 1366 |    * one | 
|---|
| 1367 |    */ | 
|---|
| 1368 |   private class GraphVisualizerMouseMotionListener extends MouseMotionAdapter { | 
|---|
| 1369 |     int x, y, nx, ny; Rectangle r; | 
|---|
| 1370 |     GraphNode lastNode; | 
|---|
| 1371 |      | 
|---|
| 1372 |     public void mouseMoved(MouseEvent me) { | 
|---|
| 1373 |       GraphNode n; | 
|---|
| 1374 |       Dimension d = m_gp.getPreferredSize(); | 
|---|
| 1375 |       //System.out.println("Preferred Size: "+this.getPreferredSize()+ | 
|---|
| 1376 |       //                   " Actual Size: "+this.getSize()); | 
|---|
| 1377 |       x=y=nx=ny=0; | 
|---|
| 1378 |        | 
|---|
| 1379 |       if(d.width < m_gp.getWidth()) | 
|---|
| 1380 |         nx = (int)((nx + m_gp.getWidth()/2 - d.width/2)/scale); | 
|---|
| 1381 |       if(d.height < m_gp.getHeight()) | 
|---|
| 1382 |         ny = (int)((ny + m_gp.getHeight()/2 - d.height/2)/scale); | 
|---|
| 1383 |        | 
|---|
| 1384 |       r=new Rectangle(0, 0,  | 
|---|
| 1385 |                      (int)(paddedNodeWidth*scale), (int)(nodeHeight*scale)); | 
|---|
| 1386 |       x += me.getX(); y += me.getY(); | 
|---|
| 1387 |        | 
|---|
| 1388 |       int i; | 
|---|
| 1389 |       for(i=0; i<m_nodes.size(); i++) { | 
|---|
| 1390 |         n = (GraphNode) m_nodes.elementAt(i); | 
|---|
| 1391 |         r.x = (int)((nx+n.x)*scale); r.y = (int)((ny+n.y)*scale); | 
|---|
| 1392 |         if(r.contains(x,y)) { | 
|---|
| 1393 |           if(n!=lastNode) { | 
|---|
| 1394 |             m_gp.highLight(n); | 
|---|
| 1395 |             if(lastNode!=null) | 
|---|
| 1396 |               m_gp.highLight(lastNode); | 
|---|
| 1397 |             lastNode = n; //lastIndex = i; | 
|---|
| 1398 |           } | 
|---|
| 1399 |           break; | 
|---|
| 1400 |         } | 
|---|
| 1401 |       } | 
|---|
| 1402 |       if(i==m_nodes.size()  && lastNode!=null) { | 
|---|
| 1403 |         m_gp.repaint(); | 
|---|
| 1404 |         //m_gp.highLight(lastNode); | 
|---|
| 1405 |         lastNode=null; | 
|---|
| 1406 |       } | 
|---|
| 1407 |     } | 
|---|
| 1408 |   } | 
|---|
| 1409 |    | 
|---|
| 1410 |   /** | 
|---|
| 1411 |    * Main method to load a text file with the | 
|---|
| 1412 |    * description of a graph from the command | 
|---|
| 1413 |    * line | 
|---|
| 1414 |    */ | 
|---|
| 1415 |   public static void main(String [] args) { | 
|---|
| 1416 |     weka.core.logging.Logger.log(weka.core.logging.Logger.Level.INFO, "Logging started"); | 
|---|
| 1417 |     JFrame jf = new JFrame("Graph Visualizer"); | 
|---|
| 1418 |     GraphVisualizer g = new GraphVisualizer(); | 
|---|
| 1419 |      | 
|---|
| 1420 |     try{ | 
|---|
| 1421 |       if(args[0].endsWith(".xml")) { | 
|---|
| 1422 |         //StringBuffer sb = new StringBuffer(); | 
|---|
| 1423 |         //FileReader infile = new FileReader(args[0]); | 
|---|
| 1424 |         //int i; | 
|---|
| 1425 |         //while( (i=infile.read())!=-1) { | 
|---|
| 1426 |         //    sb.append((char)i); | 
|---|
| 1427 |         //} | 
|---|
| 1428 |         //System.out.println(sb.toString()); | 
|---|
| 1429 |         //g.readBIF(sb.toString() ); | 
|---|
| 1430 |         g.readBIF( new FileInputStream(args[0]) ); | 
|---|
| 1431 |       } | 
|---|
| 1432 |       else { | 
|---|
| 1433 |         //BufferedReader infile=new BufferedReader(); | 
|---|
| 1434 |         g.readDOT(new FileReader(args[0])); //infile); | 
|---|
| 1435 |       } | 
|---|
| 1436 |     } | 
|---|
| 1437 |     catch(IOException ex) { ex.printStackTrace(); } | 
|---|
| 1438 |     catch(BIFFormatException bf) { bf.printStackTrace(); System.exit(-1); } | 
|---|
| 1439 |      | 
|---|
| 1440 |     jf.getContentPane().add(g); | 
|---|
| 1441 |     //RepaintManager.currentManager(jf.getRootPane()).setDoubleBufferingEnabled(false); | 
|---|
| 1442 |     jf.setDefaultCloseOperation( jf.EXIT_ON_CLOSE ); | 
|---|
| 1443 |     jf.setSize(800,600); | 
|---|
| 1444 |     //jf.pack(); | 
|---|
| 1445 |     jf.setVisible(true); | 
|---|
| 1446 |   } | 
|---|
| 1447 | } | 
|---|