source: src/main/java/weka/gui/graphvisualizer/GraphVisualizer.java @ 9

Last change on this file since 9 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 51.3 KB
Line 
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 */
22package weka.gui.graphvisualizer;
23
24import weka.core.FastVector;
25import weka.gui.ExtensionFileFilter;
26import weka.gui.visualize.PrintablePanel;
27
28import java.awt.BorderLayout;
29import java.awt.Color;
30import java.awt.Container;
31import java.awt.Dimension;
32import java.awt.Font;
33import java.awt.FontMetrics;
34import java.awt.Frame;
35import java.awt.Graphics;
36import java.awt.Graphics2D;
37import java.awt.GridBagConstraints;
38import java.awt.GridBagLayout;
39import java.awt.Insets;
40import java.awt.LayoutManager;
41import java.awt.Rectangle;
42import java.awt.RenderingHints;
43import java.awt.event.ActionEvent;
44import java.awt.event.ActionListener;
45import java.awt.event.MouseAdapter;
46import java.awt.event.MouseEvent;
47import java.awt.event.MouseMotionAdapter;
48import java.io.FileInputStream;
49import java.io.FileReader;
50import java.io.IOException;
51import java.io.InputStream;
52import java.io.Reader;
53
54import javax.swing.BorderFactory;
55import javax.swing.ImageIcon;
56import javax.swing.JButton;
57import javax.swing.JCheckBox;
58import javax.swing.JDialog;
59import javax.swing.JFileChooser;
60import javax.swing.JFrame;
61import javax.swing.JLabel;
62import javax.swing.JOptionPane;
63import javax.swing.JPanel;
64import javax.swing.JScrollPane;
65import javax.swing.JTable;
66import javax.swing.JTextField;
67import javax.swing.JToolBar;
68import 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 */
98public 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}
Note: See TracBrowser for help on using the repository browser.