Logo Search packages:      
Sourcecode: weka version File versions

GUI.java

/*
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 2 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License
 *    along with this program; if not, write to the Free Software
 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/*
 *    GUI.java
 *    Copyright (C) 2007 University of Waikato, Hamilton, New Zealand
 *
 */
package weka.classifiers.bayes.net;

import weka.core.FastVector;
import weka.core.Instances;
import weka.core.OptionHandler;
import weka.core.SerializedObject;
import weka.core.Utils;
import weka.gui.ExtensionFileFilter;
import weka.gui.GenericObjectEditor;
import weka.gui.PropertyDialog;
import weka.gui.graphvisualizer.BIFFormatException;
import weka.gui.graphvisualizer.BIFParser;
import weka.gui.graphvisualizer.GraphNode;
import weka.gui.graphvisualizer.HierarchicalBCEngine;
import weka.gui.graphvisualizer.LayoutCompleteEvent;
import weka.gui.graphvisualizer.LayoutCompleteEventListener;
import weka.gui.graphvisualizer.LayoutEngine;
import weka.gui.visualize.PrintablePanel;
import weka.classifiers.bayes.net.BIFReader;
import weka.classifiers.bayes.net.BayesNetGenerator;
import weka.classifiers.bayes.net.EditableBayesNet;
import weka.classifiers.bayes.net.MarginCalculator;
import weka.classifiers.bayes.net.MarginCalculator.JunctionTreeNode;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.beans.PropertyEditor;
import java.io.*;
import java.util.Random;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JDialog;
import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.JToolBar;
import javax.swing.KeyStroke;
import javax.swing.table.AbstractTableModel;

/**
 * GUI interface to Bayesian Networks. Allows editing Bayesian networks
 * on screen and provides GUI interface to various Bayesian network facilities
 * in Weka, including random network generation, data set generation and
 * Bayesion network inference.
 * 
 * @author Remco Bouckaert (remco@cs.waikato.ac.nz)
 * @version $Revision: 1.2 $
 */
00110 public class GUI extends JPanel implements LayoutCompleteEventListener {

      /** for serialization */
00113       private static final long serialVersionUID = -2038911085935515624L;

      /** The current LayoutEngine */
00116       protected LayoutEngine m_layoutEngine;

      /** Panel actually displaying the graph */
00119       protected GraphPanel m_GraphPanel;

      /** Container of Bayesian network */
00122       EditableBayesNet m_BayesNet = new EditableBayesNet(true);

      /** String containing file name storing current network */
00125       protected String m_sFileName = "";
      /** used for calculating marginals in Bayesian netwowrks */
00127       MarginCalculator m_marginCalculator = null;

      /**
       * used for calculating marginals in Bayesian netwowrks when evidence is
       * present
       */
00133       MarginCalculator m_marginCalculatorWithEvidence = null;

      /** flag indicating whether marginal distributions of each of the nodes 
       * should be shown in display.
       */
00138       boolean m_bViewMargins = false;
      boolean m_bViewCliques = false;

      /** data selected from file. Used to train a Bayesian network on */
00142       Instances m_Instances = null;

      /** Text field for specifying zoom */
00145       final JTextField m_jTfZoom;
      /** toolbar containing buttons at top of window */
00147       final JToolBar m_jTbTools;
      /** status bar at bottom of window */
00149       final JLabel m_jStatusBar;
      /** TextField for node's width */
00151       private final JTextField m_jTfNodeWidth = new JTextField(3);
      /** TextField for nodes height */
00153       private final JTextField m_jTfNodeHeight = new JTextField(3);
      /** this contains the m_GraphPanel GraphPanel */
00155       JScrollPane m_jScrollPane;

      /** path for icons */
00158       private final String ICONPATH = "weka/classifiers/bayes/net/icons/";

      /** current zoom value */
00161       private double m_fScale = 1;

      /** standard width of node */
00164       private int m_nNodeHeight = 2 * getFontMetrics(getFont()).getHeight();
      /** standard height of node */
00166       final static int DEFAULT_NODE_WIDTH = 50;
      private int m_nNodeWidth = DEFAULT_NODE_WIDTH;
      /** width of node, allowing for some padding */ 
00169       final static int PADDING = 10;
      private int m_nPaddedNodeWidth = DEFAULT_NODE_WIDTH + PADDING;


      /** used when using zoomIn and zoomOut buttons */
00174       private int [] m_nZoomPercents = { 10, 25, 50, 75, 100, 125, 150, 175, 200, 225, 250, 275, 300, 350, 400, 450, 500,
                  550, 600, 650, 700, 800, 900, 999 };

      /** actions triggered by GUI events */
00178       Action a_new = new ActionNew();

      Action a_quit = new ActionQuit();
      Action a_save = new ActionSave();
      ActionExport a_export = new ActionExport();
      ActionPrint a_print = new ActionPrint();
      Action a_load = new ActionLoad();
      Action a_zoomin = new ActionZoomIn();
      Action a_zoomout = new ActionZoomOut();
      Action a_layout = new ActionLayout();

      Action a_saveas = new ActionSaveAs();

      Action a_viewtoolbar = new ActionViewToolbar();

      Action a_viewstatusbar = new ActionViewStatusbar();

      Action a_networkgenerator = new ActionGenerateNetwork();

      Action a_datagenerator = new ActionGenerateData();

      Action a_datasetter = new ActionSetData();

      Action a_learn = new ActionLearn();
      Action a_learnCPT = new ActionLearnCPT();

      Action a_help = new ActionHelp();

      Action a_about = new ActionAbout();

      ActionAddNode a_addnode = new ActionAddNode();

      Action a_delnode = new ActionDeleteNode();
      Action a_cutnode = new ActionCutNode();
      Action a_copynode = new ActionCopyNode();
      Action a_pastenode = new ActionPasteNode();
      Action a_selectall = new ActionSelectAll();

      Action a_addarc = new ActionAddArc();

      Action a_delarc = new ActionDeleteArc();

      Action a_undo = new ActionUndo();

      Action a_redo= new ActionRedo();
      
      Action a_alignleft = new ActionAlignLeft();
      Action a_alignright = new ActionAlignRight();
      Action a_aligntop = new ActionAlignTop();
      Action a_alignbottom = new ActionAlignBottom();
      Action a_centerhorizontal = new ActionCenterHorizontal();
      Action a_centervertical = new ActionCenterVertical();
      Action a_spacehorizontal = new ActionSpaceHorizontal();
      Action a_spacevertical = new ActionSpaceVertical();
      
      /** node currently selected through right clicking */
00234       int m_nCurrentNode = -1;
      /** selection of nodes */
00236       Selection m_Selection = new Selection();
      /** selection rectangle drawn through dragging with left mouse button */
00238       Rectangle m_nSelectedRect = null;

      
      class Selection {
            FastVector m_selected;
            public Selection() {
                  m_selected = new FastVector();
            } // c'tor
            public FastVector getSelected() {return m_selected;}
            void updateGUI() {
                  if (m_selected.size() > 0) {
                        a_cutnode.setEnabled(true);
                        a_copynode.setEnabled(true);
                  } else {
                        a_cutnode.setEnabled(false);
                        a_copynode.setEnabled(false);
                  }
                  if (m_selected.size() > 1) {
                        a_alignleft.setEnabled(true);
                        a_alignright.setEnabled(true);
                        a_aligntop.setEnabled(true);
                        a_alignbottom.setEnabled(true);
                        a_centerhorizontal.setEnabled(true);
                        a_centervertical.setEnabled(true);
                        a_spacehorizontal.setEnabled(true);
                        a_spacevertical.setEnabled(true);
                  } else {
                        a_alignleft.setEnabled(false);
                        a_alignright.setEnabled(false);
                        a_aligntop.setEnabled(false);
                        a_alignbottom.setEnabled(false);
                        a_centerhorizontal.setEnabled(false);
                        a_centervertical.setEnabled(false);
                        a_spacehorizontal.setEnabled(false);
                        a_spacevertical.setEnabled(false);
                  }
            } // updateGUI
            
            public void addToSelection(int nNode) {
                  for (int iNode = 0; iNode < m_selected.size(); iNode++) {
                        if (nNode == (Integer) m_selected.elementAt(iNode)) {
                              return;
                        }
                  }
                  m_selected.addElement(nNode);
                  updateGUI();
            } // addToSelection
            
            public void addToSelection(int [] iNodes) {
                  for (int iNode = 0; iNode < iNodes.length; iNode++) {
                        addToSelection(iNodes[iNode]);
                  }
                  updateGUI();
            } // addToSelection
            
            public void addToSelection(Rectangle selectedRect) {
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        if (contains(selectedRect, iNode)) {
                              addToSelection(iNode);
                        }
                  }
            } // addToSelection
            
            public void selectAll() {
                  m_selected.removeAllElements();
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        m_selected.addElement(iNode);
                  }
                  updateGUI();
            } // selectAll

            boolean contains(Rectangle rect, int iNode) {
                  return rect.intersects((m_BayesNet.getPositionX(iNode)) * m_fScale,
                              (m_BayesNet.getPositionY(iNode)) * m_fScale,
                              m_nPaddedNodeWidth * m_fScale, m_nNodeHeight * m_fScale);
            } // contains
            
            public void removeFromSelection(int nNode) {
                  for (int iNode = 0; iNode < m_selected.size(); iNode++) {
                        if (nNode == (Integer) m_selected.elementAt(iNode)) {
                              m_selected.removeElementAt(iNode);
                        }
                  }
                  updateGUI();
            } // removeFromSelection

            public void toggleSelection(int nNode) {
                  for (int iNode = 0; iNode < m_selected.size(); iNode++) {
                        if (nNode == (Integer) m_selected.elementAt(iNode)) {
                              m_selected.removeElementAt(iNode);
                              updateGUI();
                              return;
                        }
                  }
                  addToSelection(nNode);
            } // toggleSelection

            public void toggleSelection(Rectangle selectedRect)   {
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        if (contains(selectedRect, iNode)) {
                              toggleSelection(iNode);
                        }
                  }
            } // toggleSelection
            
            public void clear() {
                  m_selected.removeAllElements();
                  updateGUI();
            }
            
            public void draw(Graphics g) {
                  if (m_selected.size() == 0) {
                        return;
                  }

                  for (int iNode = 0; iNode < m_selected.size(); iNode++) {
                        int nNode = (Integer) m_selected.elementAt(iNode);
                        int nPosX = m_BayesNet.getPositionX(nNode);
                        int nPosY = m_BayesNet.getPositionY(nNode);
                        g.setColor(Color.BLACK);
                        int nXRC = nPosX + m_nPaddedNodeWidth - m_nNodeWidth - (m_nPaddedNodeWidth - m_nNodeWidth) / 2;
                        int nYRC = nPosY;
                        int d = 5;
                        g.fillRect(nXRC, nYRC, d, d); 
                        g.fillRect(nXRC, nYRC + m_nNodeHeight, d, d); 
                        g.fillRect(nXRC + m_nNodeWidth, nYRC, d, d); 
                        g.fillRect(nXRC + m_nNodeWidth, nYRC + m_nNodeHeight, d, d); 
                  }
            } // draw
      } // Selection

      ClipBoard m_clipboard = new ClipBoard();
      
      class ClipBoard {
            String m_sText = null;
            public ClipBoard() {
                  if (a_pastenode != null) {
                        a_pastenode.setEnabled(false);
                  }
            }
            public boolean hasText() {return m_sText != null;}
            public String getText() { 
                  return m_sText;
            }
            public void setText(String sText) {
                  m_sText = sText;
                  a_pastenode.setEnabled(true);
            }
      } // class ClipBoard
      
      /** Base class used for definining actions
       * with a name, tool tip text, possibly an icon and accelerator key. 
       * */
00391       class MyAction extends AbstractAction {
            /** for serialization */
00393             private static final long serialVersionUID = -2038911111935517L;

            public MyAction(String sName, String sToolTipText, String sIcon, String sAcceleratorKey) {
                  super(sName);
                  //setToolTipText(sToolTipText);
                  putValue(Action.SHORT_DESCRIPTION, sToolTipText);
                  putValue(Action.LONG_DESCRIPTION, sToolTipText);
                  if (sAcceleratorKey.length() > 0) {
                        KeyStroke keyStroke = KeyStroke.getKeyStroke(sAcceleratorKey);
                        putValue(Action.ACCELERATOR_KEY, keyStroke);
                  }
                  putValue(Action.MNEMONIC_KEY, (int) sName.charAt(0));
                  java.net.URL tempURL = ClassLoader.getSystemResource(ICONPATH + sIcon + ".png");
                  if (tempURL != null) {
                        putValue(Action.SMALL_ICON, new ImageIcon(tempURL));
                  } else {
                        putValue(Action.SMALL_ICON, new ImageIcon(new BufferedImage(20,20, BufferedImage.TYPE_4BYTE_ABGR)));
                        //System.err.println(ICONPATH + sIcon + ".png not found for weka.gui.graphvisualizer.Graph");
                  }
            } // c'tor

            /* Place holder. Should be implemented by derived classes.
             *  (non-Javadoc)
             * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
             */
            public void actionPerformed(ActionEvent ae) {}
      } // class MyAction

      class ActionGenerateNetwork extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038911085935517L;

            public ActionGenerateNetwork() {
                  super("Generate Network", "Generate Random Bayesian Network", "generate.network", "ctrl N");
            } // c'tor

            int m_nNrOfNodes = 10;

            int m_nNrOfArcs = 15;

            int m_nCardinality = 2;

            int m_nSeed = 123;

            JDialog dlg = null;

            public void actionPerformed(ActionEvent ae) {
                  if (dlg == null) {
                        dlg = new JDialog();
                        dlg.setTitle("Generate Random Bayesian Network Options");

                        final JLabel jLbNrOfNodes = new JLabel("Nr of nodes");
                        final JTextField jTfNrOfNodes = new JTextField(3);
                        jTfNrOfNodes.setHorizontalAlignment(JTextField.CENTER);
                        jTfNrOfNodes.setText("" + m_nNrOfNodes);
                        final JLabel jLbNrOfArcs = new JLabel("Nr of arcs");
                        final JTextField jTfNrOfArcs = new JTextField(3);
                        jTfNrOfArcs.setHorizontalAlignment(JTextField.CENTER);
                        jTfNrOfArcs.setText("" + m_nNrOfArcs);
                        final JLabel jLbCardinality = new JLabel("Cardinality");
                        final JTextField jTfCardinality = new JTextField(3);
                        jTfCardinality.setHorizontalAlignment(JTextField.CENTER);
                        jTfCardinality.setText("" + m_nCardinality);
                        final JLabel jLbSeed = new JLabel("Random seed");
                        final JTextField jTfSeed = new JTextField(3);
                        jTfSeed.setHorizontalAlignment(JTextField.CENTER);
                        jTfSeed.setText("" + m_nSeed);

                        JButton jBtGo;
                        jBtGo = new JButton("Generate Network");

                        jBtGo.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    try {
                                          BayesNetGenerator generator = new BayesNetGenerator();
                                          m_BayesNet = generator;
                                          m_BayesNet.clearUndoStack();
                                          
                                          String[] options = new String[8];
                                          options[0] = "-N";
                                          options[1] = "" + jTfNrOfNodes.getText();
                                          options[2] = "-A";
                                          options[3] = "" + jTfNrOfArcs.getText();
                                          options[4] = "-C";
                                          options[5] = "" + jTfCardinality.getText();
                                          options[6] = "-S";
                                          options[7] = "" + jTfSeed.getText();
                                          generator.setOptions(options);
                                          generator.generateRandomNetwork();
                                          // Convert to EditableBayesNet
                                          // This ensures the getOptions() called by GenericObjectEditor to get the correct result.
                                          BIFReader bifReader = new BIFReader();
                                          bifReader.processString(m_BayesNet.toXMLBIF03());
                                          m_BayesNet = new EditableBayesNet(bifReader);

                                          updateStatus();
                                          layoutGraph();
                                          a_datagenerator.setEnabled(true);
                                          m_Instances = null;;
                                          a_learn.setEnabled(false);
                                          a_learnCPT.setEnabled(false);

                                          dlg.setVisible(false);
                                    } catch (Exception e) {
                                          e.printStackTrace();
                                    }
                              }
                        });

                        JButton jBtCancel;
                        jBtCancel = new JButton("Cancel");
                        jBtCancel.setMnemonic('C');
                        jBtCancel.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    dlg.setVisible(false);
                              }
                        });
                        GridBagConstraints gbc = new GridBagConstraints();
                        dlg.setLayout(new GridBagLayout());

                        Container c = new Container();
                        c.setLayout(new GridBagLayout());
                        gbc.gridwidth = 2;
                        gbc.insets = new Insets(8, 0, 0, 0);
                        gbc.anchor = GridBagConstraints.NORTHWEST;
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        gbc.fill = GridBagConstraints.HORIZONTAL;
                        c.add(jLbNrOfNodes, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jTfNrOfNodes, gbc);
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        c.add(jLbNrOfArcs, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jTfNrOfArcs, gbc);
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        c.add(jLbCardinality, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jTfCardinality, gbc);
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        c.add(jLbSeed, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jTfSeed, gbc);

                        gbc.fill = GridBagConstraints.HORIZONTAL;
                        dlg.add(c, gbc);
                        dlg.add(jBtGo);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        dlg.add(jBtCancel);
                  }
                  dlg.setLocation(100, 100);
                  dlg.setVisible(true);
                  dlg.setSize(dlg.getPreferredSize());
                  dlg.setVisible(false);
                  dlg.setVisible(true);
                  dlg.repaint();
            } // actionPerformed
      } // class ActionGenerate

      class ActionGenerateData extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038911085935516L;

            public ActionGenerateData() {
                  super("Generate Data", "Generate Random Instances from Network", "generate.data", "ctrl D");
            } // c'tor

            int m_nNrOfInstances = 100;

            int m_nSeed = 1234;

            String m_sFile = "";

            JDialog dlg = null;

            public void actionPerformed(ActionEvent ae) {
                  if (dlg == null) {
                        dlg = new JDialog();
                        dlg.setTitle("Generate Random Data Options");

                        final JLabel jLbNrOfInstances = new JLabel("Nr of instances");
                        final JTextField jTfNrOfInstances = new JTextField(3);
                        jTfNrOfInstances.setHorizontalAlignment(JTextField.CENTER);
                        jTfNrOfInstances.setText("" + m_nNrOfInstances);
                        final JLabel jLbSeed = new JLabel("Random seed");
                        final JTextField jTfSeed = new JTextField(3);
                        jTfSeed.setHorizontalAlignment(JTextField.CENTER);
                        jTfSeed.setText("" + m_nSeed);
                        final JLabel jLbFile = new JLabel("Output file (optional)");
                        final JTextField jTfFile = new JTextField(12);
                        jTfFile.setHorizontalAlignment(JTextField.CENTER);
                        jTfFile.setText(m_sFile);

                        JButton jBtGo;
                        jBtGo = new JButton("Generate Data");

                        jBtGo.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    try {
                                          String tmpfilename = "tmp.bif.file.xml";
                                          BayesNetGenerator generator = new BayesNetGenerator();
                                          String[] options = new String[4];
                                          options[0] = "-M";
                                          options[1] = "" + jTfNrOfInstances.getText();
                                          options[2] = "-F";
                                          options[3] = tmpfilename;
                                          FileWriter outfile = new FileWriter(tmpfilename);
                                          StringBuffer text = new StringBuffer();
                                          if (m_marginCalculator == null) {
                                                m_marginCalculator = new MarginCalculator();
                                                m_marginCalculator.calcMargins(m_BayesNet);
                                          }
                                          text.append(m_marginCalculator.toXMLBIF03());
                                          outfile.write(text.toString());
                                          outfile.close();

                                          generator.setOptions(options);
                                          generator.generateRandomNetwork();
                                          generator.generateInstances();
                                          m_Instances = generator.m_Instances;
                                          a_learn.setEnabled(true);
                                          a_learnCPT.setEnabled(true);

                                          m_sFile = jTfFile.getText();
                                          if (m_sFile != null && !m_sFile.equals("")) {
                                                FileWriter outfile2 = new FileWriter(m_sFile);
                                                StringBuffer text2 = new StringBuffer();
                                                text2.append(m_Instances.toString());
                                                outfile2.write(text2.toString());
                                                outfile2.close();
                                          }

                                    } catch (Exception e) {
                                          e.printStackTrace();
                                    }
                                    dlg.setVisible(false);
                              }
                        });

                        JButton jBtFile = new JButton("Browse");
                        ;
                        jBtFile.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
                                    ExtensionFileFilter ef1 = new ExtensionFileFilter(".arff", "Arff files");
                                    fc.addChoosableFileFilter(ef1);
                                    fc.setDialogTitle("Save Instances As");
                                    int rval = fc.showSaveDialog(GUI.this);

                                    if (rval == JFileChooser.APPROVE_OPTION) {
                                          String filename = fc.getSelectedFile().toString();
                                          jTfFile.setText(filename);
                                    }
                                    dlg.setVisible(true);
                              }
                        });
                        JButton jBtCancel;
                        jBtCancel = new JButton("Cancel");
                        jBtCancel.setMnemonic('C');
                        jBtCancel.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    dlg.setVisible(false);
                              }
                        });
                        GridBagConstraints gbc = new GridBagConstraints();
                        dlg.setLayout(new GridBagLayout());

                        Container c = new Container();
                        c.setLayout(new GridBagLayout());
                        gbc.gridwidth = 2;
                        gbc.insets = new Insets(8, 0, 0, 0);
                        gbc.anchor = GridBagConstraints.NORTHWEST;
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        gbc.fill = GridBagConstraints.HORIZONTAL;
                        c.add(jLbNrOfInstances, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jTfNrOfInstances, gbc);
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        c.add(jLbSeed, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jTfSeed, gbc);
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        c.add(jLbFile, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jTfFile, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jBtFile, gbc);

                        gbc.fill = GridBagConstraints.HORIZONTAL;
                        dlg.add(c, gbc);
                        dlg.add(jBtGo);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        dlg.add(jBtCancel);
                  }
                  dlg.setLocation(100, 100);
                  dlg.setVisible(true);
                  dlg.setSize(dlg.getPreferredSize());
                  dlg.setVisible(false);
                  dlg.setVisible(true);
                  dlg.repaint();

            } // actionPerformed
      } // class ActionGenerateData

      class ActionLearn extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038911085935516L;

            public ActionLearn() {
                  super("Learn Network", "Learn Bayesian Network", "learn", "ctrl L");
                  setEnabled(false);
            } // c'tor

            JDialog dlg = null;

            public void actionPerformed(ActionEvent ae) {
                  if (dlg == null) {
                        dlg = new JDialog();
                        dlg.setTitle("Learn Bayesian Network");

                        final JButton jBtOptions = new JButton("Options");
                        jBtOptions.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    //m_BayesNet = new EditableBayesNet();
                                    try {
                                          GenericObjectEditor.registerEditors();
                                          GenericObjectEditor ce = new GenericObjectEditor(true);
                                          ce.setClassType(weka.classifiers.Classifier.class);
                                          ce.setValue(m_BayesNet);

                                          PropertyDialog pd = new PropertyDialog(ce, 100, 100);
                                          pd.addWindowListener(new WindowAdapter() {
                                                public void windowClosing(WindowEvent e) {
                                                      PropertyEditor pe = ((PropertyDialog) e.getSource()).getEditor();
                                                      Object c = (Object) pe.getValue();
                                                      String options = "";
                                                      if (c instanceof OptionHandler) {
                                                            options = Utils.joinOptions(((OptionHandler) c).getOptions());
                                                            try {
                                                                  m_BayesNet.setOptions(((OptionHandler) c).getOptions());
                                                            } catch (Exception e2) {
                                                                  e2.printStackTrace();
                                                            }
                                                      }
                                                      System.out.println(c.getClass().getName() + " " + options);
                                                      System.exit(0);
                                                }
                                          });
                                    } catch (Exception ex) {
                                          ex.printStackTrace();
                                          System.err.println(ex.getMessage());
                                    }
                                    m_BayesNet.clearUndoStack();
                                    a_undo.setEnabled(false);
                                    a_redo.setEnabled(false);
                              }
                        });

                        final JTextField jTfOptions = new JTextField(40);
                        jTfOptions.setHorizontalAlignment(JTextField.CENTER);
                        jTfOptions.setText("" + Utils.joinOptions(m_BayesNet.getOptions()));

                        JButton jBtGo;
                        jBtGo = new JButton("Learn");

                        jBtGo.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    try {
                                          m_BayesNet.buildClassifier(m_Instances);
                                          layoutGraph();
                                          updateStatus();
                                          m_BayesNet.clearUndoStack();

                                          dlg.setVisible(false);
                                    } catch (Exception e) {
                                          e.printStackTrace();
                                    }
                                    dlg.setVisible(false);
                              }
                        });

                        JButton jBtCancel;
                        jBtCancel = new JButton("Cancel");
                        jBtCancel.setMnemonic('C');
                        jBtCancel.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    dlg.setVisible(false);
                              }
                        });
                        GridBagConstraints gbc = new GridBagConstraints();
                        dlg.setLayout(new GridBagLayout());

                        Container c = new Container();
                        c.setLayout(new GridBagLayout());
                        gbc.gridwidth = 2;
                        gbc.insets = new Insets(8, 0, 0, 0);
                        gbc.anchor = GridBagConstraints.NORTHWEST;
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        gbc.fill = GridBagConstraints.HORIZONTAL;
                        c.add(jBtOptions, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jTfOptions, gbc);

                        gbc.fill = GridBagConstraints.HORIZONTAL;
                        dlg.add(c, gbc);
                        dlg.add(jBtGo);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        dlg.add(jBtCancel);
                  }
                  dlg.setLocation(100, 100);
                  dlg.setVisible(true);
                  dlg.setSize(dlg.getPreferredSize());
                  dlg.setVisible(false);
                  dlg.setVisible(true);
                  dlg.repaint();
            } // actionPerformed
      } // class ActionLearn

      class ActionLearnCPT extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2022211085935516L;

            public ActionLearnCPT() {
                  super("Learn CPT", "Learn conditional probability tables", "learncpt", "");
                  setEnabled(false);
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  if (m_Instances == null) {
                        JOptionPane.showMessageDialog(null, "Select instances to learn from first (menu Tools/Set Data)");
                        return;
                  }
                  try {
                        m_BayesNet.setData(m_Instances);
                  } catch (Exception e) {
                        JOptionPane.showMessageDialog(null, "Data set is not compatible with network.\n"+e.getMessage() + "\nChoose other instances (menu Tools/Set Data)");
                        return;
                  }
                  try {
                        m_BayesNet.estimateCPTs();
                        m_BayesNet.clearUndoStack();
                  } catch (Exception e) {
                        e.printStackTrace();
                  }
                  updateStatus();
            } // actionPerformed
      } // class ActionLearnCPT

      class ActionSetData extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038911085935519L;

            public ActionSetData() {
                  super("Set Data", "Set Data File", "setdata", "ctrl A");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
                  ExtensionFileFilter ef1 = new ExtensionFileFilter(".arff", "ARFF files");
                  fc.addChoosableFileFilter(ef1);
                  fc.setDialogTitle("Set Data File");
                  int rval = fc.showOpenDialog(GUI.this);

                  if (rval == JFileChooser.APPROVE_OPTION) {
                        String filename = fc.getSelectedFile().toString();
                        try {
                              m_Instances = new Instances(new FileReader(filename));
                              m_Instances.setClassIndex(m_Instances.numAttributes() - 1);
                              a_learn.setEnabled(true);
                              a_learnCPT.setEnabled(true);
                              repaint();
                        } catch (Exception e) {
                              e.printStackTrace();
                        }
                  }
            }
      } // class ActionSetData

      class ActionUndo extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -3038910085935519L;

            public ActionUndo() {
                  super("Undo", "Undo", "undo", "ctrl Z");
                  setEnabled(false);
            } // c'tor

            public boolean isEnabled() {
                  return m_BayesNet.canUndo();
            }

            public void actionPerformed(ActionEvent ae) {
                  String sMsg = m_BayesNet.undo();
                  m_jStatusBar.setText("Undo action performed: " + sMsg);
                  //if (!sMsg.equals("")) {
                  //    JOptionPane.showMessageDialog(null, sMsg, "Undo action successful", JOptionPane.INFORMATION_MESSAGE);
                  //}
                  a_redo.setEnabled(m_BayesNet.canRedo());
                  a_undo.setEnabled(m_BayesNet.canUndo());
                  m_Selection.clear();
                  updateStatus();
                  repaint();
            }
      } // ActionUndo
      
      class ActionRedo extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -4038910085935519L;

            public ActionRedo() {
                  super("Redo", "Redo", "redo", "ctrl Y");
                  setEnabled(false);
            } // c'tor

            public boolean isEnabled() {
                  return m_BayesNet.canRedo();
            }

            public void actionPerformed(ActionEvent ae) {
                  String sMsg = m_BayesNet.redo();
                  m_jStatusBar.setText("Redo action performed: " + sMsg);
                  //if (!sMsg.equals("")) {
                  //    JOptionPane.showMessageDialog(null, sMsg, "Redo action successful", JOptionPane.INFORMATION_MESSAGE);
                  //}
                  m_Selection.clear();
                  updateStatus();
                  repaint();
            }
      } // ActionRedo

      class ActionAddNode extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038910085935519L;

            public ActionAddNode() {
                  super("Add Node", "Add Node", "addnode", "");
            } // c'tor

            JDialog dlg = null;

            JTextField jTfName = new JTextField(20);

            JTextField jTfCard = new JTextField(3);

            int m_X = Integer.MAX_VALUE;
            int m_Y;
            public void addNode(int nX, int nY) {
                  m_X = nX;
                  m_Y = nY;
                  addNode();
            } // addNode
            
            void addNode() {
                  if (dlg == null) {
                        dlg = new JDialog();
                        dlg.setTitle("Add node");
                        JLabel jLbName = new JLabel("Name");
                        jTfName.setHorizontalAlignment(JTextField.CENTER);
                        JLabel jLbCard = new JLabel("Cardinality");
                        jTfCard.setHorizontalAlignment(JTextField.CENTER);
                        jTfCard.setText("2");

                        JButton jBtCancel;
                        jBtCancel = new JButton("Cancel");
                        jBtCancel.setMnemonic('C');
                        jBtCancel.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    dlg.setVisible(false);
                              }
                        });
                        JButton jBtOk = new JButton("Ok");
                        jBtOk.setMnemonic('O');
                        jBtOk.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    String sName = jTfName.getText();
                                    if (sName.length() <= 0) {
                                          JOptionPane.showMessageDialog(null, "Name should have at least one character");
                                          return;
                                    }
                                    int nCard = new Integer(jTfCard.getText()).intValue();
                                    if (nCard <= 1) {
                                          JOptionPane.showMessageDialog(null, "Cardinality should be larger than 1");
                                          return;
                                    }
                                    try {
                                          if (m_X < Integer.MAX_VALUE) {
                                                m_BayesNet.addNode(sName, nCard, m_X, m_Y);
                                          } else {
                                                m_BayesNet.addNode(sName, nCard);
                                          }
                                        m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                                          a_undo.setEnabled(true);
                                          a_redo.setEnabled(false);
                                          //GraphNode n = new GraphNode("id" + m_nodes.size(), sName);
                                          //n.probs = m_BayesNet.getDistribution(sName);
                                          //n.outcomes = m_BayesNet.getValues(sName);
                                          //n.x = 100 + m_nodes.size() * 10;
                                          //n.y = 100 + m_nodes.size() * 10;
                                          //m_nodes.addElement(n);
                                    } catch (Exception e) {
                                          e.printStackTrace();
                                    }
                                    repaint();
                                    dlg.setVisible(false);
                              }
                        });
                        dlg.setLayout(new GridLayout(3, 2, 10, 10));
                        dlg.add(jLbName);
                        dlg.add(jTfName);
                        dlg.add(jLbCard);
                        dlg.add(jTfCard);
                        dlg.add(jBtOk);
                        dlg.add(jBtCancel);
                        dlg.setSize(dlg.getPreferredSize());
                  }
                  jTfName.setText("Node" + (m_BayesNet.getNrOfNodes() + 1));
                  dlg.setVisible(true);
            } // addNode
            
            public void actionPerformed(ActionEvent ae) {
                  m_X = Integer.MAX_VALUE;
                  addNode();
            }
      } // class ActionAddNode

      class ActionDeleteNode extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038912085935519L;

            public ActionDeleteNode() {
                  super("Delete Node", "Delete Node", "delnode", "DELETE");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  if (m_Selection.getSelected().size() > 0) {
                        m_BayesNet.deleteSelection(m_Selection.getSelected());
                      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                        m_Selection.clear();
                        updateStatus();
                        repaint();
                  } else {
                        String[] options = new String[m_BayesNet.getNrOfNodes()];
                        for (int i = 0; i < options.length; i++) {
                              options[i] = m_BayesNet.getNodeName(i);
                        }
                        String sResult = (String) JOptionPane.showInputDialog(null, "Select node to delete", "Nodes", 0, null,
                                    options, options[0]);
                        if (sResult != null && !sResult.equals("")) {
                              int iNode = m_BayesNet.getNode2(sResult);
                              deleteNode(iNode);
                        }
                  }
            }
      } // class ActionDeleteNode

      class ActionCopyNode extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038732085935519L;

            public ActionCopyNode() {
                  super("Copy", "Copy Nodes", "copy", "ctrl C");
            } // c'tor
            
            public ActionCopyNode(String sName, String sToolTipText, String sIcon, String sAcceleratorKey) {
                  super(sName, sToolTipText, sIcon, sAcceleratorKey);
            } // c'rot
            
            public void actionPerformed(ActionEvent ae) {
                  copy();
            }
            
            public void copy() {
                  String sXML = m_BayesNet.toXMLBIF03(m_Selection.getSelected());
                  m_clipboard.setText(sXML);
            } // copy
      } // class ActionCopyNode

      class ActionCutNode extends ActionCopyNode {
            /** for serialization */
            private static final long serialVersionUID = -2038822085935519L;

            public ActionCutNode() {
                  super("Cut", "Cut Nodes", "cut", "ctrl X");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  copy();
                  m_BayesNet.deleteSelection(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  m_Selection.clear();
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionCutNode

      class ActionPasteNode extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038732085935519L;

            public ActionPasteNode() {
                  super("Paste", "Paste Nodes", "paste", "ctrl V");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  try {
                        m_BayesNet.paste(m_clipboard.getText());
                        updateStatus();
                      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  } catch (Exception e) {
                        e.printStackTrace();
                  }
            }
            public boolean isEnabled() {
                  return m_clipboard.hasText();
            }
      } // class ActionPasteNode

      class ActionSelectAll extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038642085935519L;

            public ActionSelectAll() {
                  super("Select All", "Select All Nodes", "selectall", "ctrl A");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_Selection.selectAll();
                  repaint();
            }
      } // class ActionSelectAll

      class ActionExport extends MyAction {
            boolean m_bIsExporting = false;           
            /** for serialization */
            private static final long serialVersionUID = -3027642085935519L;

            public ActionExport() {
                  super("Export", "Export to graphics file", "export", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_bIsExporting = true;
                  m_GraphPanel.saveComponent();
                  m_bIsExporting = false;
                  repaint();
            }
            public boolean isExporting() {return m_bIsExporting;}
      } // class ActionExport

      class ActionAlignLeft extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -3138642085935519L;

            public ActionAlignLeft() {
                  super("Align Left", "Align Left", "alignleft", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_BayesNet.alignLeft(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionAlignLeft
      
      class ActionAlignRight extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -4238642085935519L;

            public ActionAlignRight() {
                  super("Align Right", "Align Right", "alignright", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_BayesNet.alignRight(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionAlignRight

      class ActionAlignTop extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -5338642085935519L;

            public ActionAlignTop() {
                  super("Align Top", "Align Top", "aligntop", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_BayesNet.alignTop(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionAlignTop

      class ActionAlignBottom extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -6438642085935519L;

            public ActionAlignBottom() {
                  super("Align Bottom", "Align Bottom", "alignbottom", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_BayesNet.alignBottom(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionAlignBottom

      class ActionCenterHorizontal extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -7538642085935519L;

            public ActionCenterHorizontal() {
                  super("Center Horizontal", "Center Horizontal", "centerhorizontal", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_BayesNet.centerHorizontal(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionCenterHorizontal

      class ActionCenterVertical extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -8638642085935519L;

            public ActionCenterVertical() {
                  super("Center Vertical", "Center Vertical", "centervertical", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_BayesNet.centerVertical(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionCenterVertical

      class ActionSpaceHorizontal extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -9738642085935519L;

            public ActionSpaceHorizontal() {
                  super("Space Horizontal", "Space Horizontal", "spacehorizontal", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_BayesNet.spaceHorizontal(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionSpaceHorizontal

      class ActionSpaceVertical extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -838642085935519L;

            public ActionSpaceVertical() {
                  super("Space Vertical", "Space Vertical", "spacevertical", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_BayesNet.spaceVertical(m_Selection.getSelected());
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  a_undo.setEnabled(true);
                  a_redo.setEnabled(false);
                  repaint();
            }
      } // class ActionSpaceVertical

      class ActionAddArc extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038913085935519L;

            public ActionAddArc() {
                  super("Add Arc", "Add Arc", "addarc", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  try {
                        String[] options = new String[m_BayesNet.getNrOfNodes()];
                        for (int i = 0; i < options.length; i++) {
                              options[i] = m_BayesNet.getNodeName(i);
                        }
                        String sChild = (String) JOptionPane.showInputDialog(null, "Select child node", "Nodes", 0, null,
                                    options, options[0]);
                        if (sChild == null || sChild.equals("")) {
                              return;
                        }
                        int iChild = m_BayesNet.getNode(sChild);
                        addArcInto(iChild);
                  } catch (Exception e) {
                        e.printStackTrace();
                  }
            }
      } // class ActionAddArc

      class ActionDeleteArc extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038914085935519L;

            public ActionDeleteArc() {
                  super("Delete Arc", "Delete Arc", "delarc", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  int nEdges = 0;
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        nEdges += m_BayesNet.getNrOfParents(iNode);
                  }
                  String[] options = new String[nEdges];
                  int i = 0;
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
                              int nParent = m_BayesNet.getParent(iNode, iParent);
                              String sEdge = m_BayesNet.getNodeName(nParent);
                              sEdge += " -> ";
                              sEdge += m_BayesNet.getNodeName(iNode);
                              options[i++] = sEdge;
                        }
                        
                  }
                  deleteArc(options);
            }
      } // class ActionDeleteArc

      class ActionNew extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038911085935515L;

            public ActionNew() {
                  super("New", "New Network", "new", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_sFileName = "";
                  m_BayesNet = new EditableBayesNet(true);
                  updateStatus();
                  layoutGraph();
                  a_datagenerator.setEnabled(false);
                  m_BayesNet.clearUndoStack();
                m_jStatusBar.setText("New Network");
                  m_Selection = new Selection();
                  repaint();
            }
      } // class ActionNew

      class ActionLoad extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038911085935515L;

            public ActionLoad() {
                  super("Load", "Load Graph", "open", "ctrl O");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
                  ExtensionFileFilter ef1 = new ExtensionFileFilter(".arff", "ARFF files");
                  ExtensionFileFilter ef2 = new ExtensionFileFilter(".xml", "XML BIF files");
                  fc.addChoosableFileFilter(ef1);
                  fc.addChoosableFileFilter(ef2);
                  fc.setDialogTitle("Load Graph");
                  int rval = fc.showOpenDialog(GUI.this);

                  if (rval == JFileChooser.APPROVE_OPTION) {
                        String sFileName = fc.getSelectedFile().toString();
                        if (sFileName.endsWith(ef1.getExtensions()[0])) {
                              initFromArffFile(sFileName);
                        } else {
                              try {
                                    readBIFFromFile(sFileName);
                              } catch (Exception e) {
                                    e.printStackTrace();
                              }
                        }
                      m_jStatusBar.setText("Loaded " + sFileName);
                      updateStatus();
                  }
            }
      } // class ActionLoad
      
      
      class ActionViewStatusbar extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -20389330812354L;

            public ActionViewStatusbar() {
                  super("View statusbar", "View statusbar", "statusbar", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_jStatusBar.setVisible(!m_jStatusBar.isVisible());
            } // actionPerformed
      } // class ActionViewStatusbar

      class ActionViewToolbar extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -20389110812354L;

            public ActionViewToolbar() {
                  super("View toolbar", "View toolbar", "toolbar", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  m_jTbTools.setVisible(!m_jTbTools.isVisible());
            } // actionPerformed
      } // class ActionViewToolbar

      class ActionSave extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -20389110859355156L;

            public ActionSave() {
                  super("Save", "Save Graph", "save", "ctrl S");
            } // c'tor

            public ActionSave(String sName, String sToolTipText, String sIcon, String sAcceleratorKey) {
                  super(sName, sToolTipText, sIcon, sAcceleratorKey);
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  if (!m_sFileName.equals("")) {
                        saveFile(m_sFileName);
                        m_BayesNet.isSaved();
                      m_jStatusBar.setText("Saved as " + m_sFileName);
                  } else {
                        if (saveAs()) {
                              m_BayesNet.isSaved();
                            m_jStatusBar.setText("Saved as " + m_sFileName);                          
                        }
                  }
            } // actionPerformed


            ExtensionFileFilter ef1 = new ExtensionFileFilter(".xml", "XML BIF files");

            boolean saveAs() {
                  JFileChooser fc = new JFileChooser(System.getProperty("user.dir"));
                  fc.addChoosableFileFilter(ef1);
                  fc.setDialogTitle("Save Graph As");
                  if (!m_sFileName.equals("")) {
                        // can happen on actionQuit
                        fc.setSelectedFile(new File(m_sFileName));
                  }
                  int rval = fc.showSaveDialog(GUI.this);

                  if (rval == JFileChooser.APPROVE_OPTION) {
                        // System.out.println("Saving to file \""+
                        // f.getAbsoluteFile().toString()+"\"");
                        String sFileName = fc.getSelectedFile().toString();
                        if (!sFileName.endsWith(".xml"))
                              sFileName = sFileName.concat(".xml");
                        saveFile(sFileName);
                        return true;
                  }
                  return false;
            } // saveAs

            protected void saveFile(String sFileName) {
                try {
                    FileWriter outfile = new FileWriter(sFileName);
                    outfile.write(m_BayesNet.toXMLBIF03());
                    outfile.close();
                        m_sFileName = sFileName;
                      m_jStatusBar.setText("Saved as " + m_sFileName);
                  }
                  catch(IOException e) { 
                    e.printStackTrace(); 
                  }
              } // saveFile
      } // class ActionSave
      
      class ActionSaveAs extends ActionSave {
            /** for serialization */
            private static final long serialVersionUID = -20389110859354L;

            public ActionSaveAs() {
                  super("Save As", "Save Graph As", "saveas", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  saveAs();
            } // actionPerformed
      } // class ActionSaveAs

      class ActionPrint extends ActionSave {
            /** for serialization */
            private static final long serialVersionUID = -20389001859354L;
            boolean m_bIsPrinting = false;
            public ActionPrint() {
                  super("Print", "Print Graph", "print", "ctrl P");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                PrinterJob printJob = PrinterJob.getPrinterJob();
                printJob.setPrintable(m_GraphPanel);
                if (printJob.printDialog())
                  try { 
                        m_bIsPrinting = true;
                    printJob.print();
                        m_bIsPrinting = false;
                  } catch(PrinterException pe) {
                    m_jStatusBar.setText("Error printing: " + pe);
                        m_bIsPrinting = false;
                  }
                      m_jStatusBar.setText("Print");
            } // actionPerformed
            public boolean isPrinting() {return m_bIsPrinting;}

      } // class ActionPrint

      class ActionQuit extends ActionSave {
            /** for serialization */
            private static final long serialVersionUID = -2038911085935515L;

            public ActionQuit() {
                  super("Exit", "Exit Program", "exit", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  if (m_BayesNet.isChanged()) {
                        int result = JOptionPane.showConfirmDialog(null, "Network changed. Do you want to save it?", "Save before closing?", JOptionPane.YES_NO_CANCEL_OPTION);
                        if (result == JOptionPane.CANCEL_OPTION) {
                              return;
                        }
                        if (result == JOptionPane.YES_OPTION) {
                              if (!saveAs()) {
                                    return;
                              }
                        }
                  }
                  System.exit(0);
            }
      } // class ActionQuit

      class ActionHelp extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -20389110859354L;

            public ActionHelp() {
                  super("Help", "Bayesian Network Workbench Help", "help", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  JOptionPane.showMessageDialog(null, "See Weka Homepage\nhttp://www.cs.waikato.ac.nz/ml", "Help Message",
                              JOptionPane.PLAIN_MESSAGE);
            }
      } // class ActionHelp

      class ActionAbout extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -20389110859353L;

            public ActionAbout() {
                  super("About", "Help about", "about", "");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  JOptionPane.showMessageDialog(null, "Bayesian Network Workbench\nPart of Weka\n2007", "About Message",
                              JOptionPane.PLAIN_MESSAGE);
            }
      } // class ActionAbout

      class ActionZoomIn extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -2038911085935515L;

            public ActionZoomIn() {
                  super("Zoom in", "Zoom in", "zoomin", "+");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  int i = 0, s = (int) (m_fScale * 100);
                  if (s < 300)
                        i = s / 25;
                  else if (s < 700)
                        i = 6 + s / 50;
                  else
                        i = 13 + s / 100;

                  if (s >= 999) {
                        setEnabled(false);
                        return;
                  } else if (s >= 10) {
                        if (i >= 22) {
                              setEnabled(false);
                        }
                        if (s == 10 && !a_zoomout.isEnabled()) {
                              a_zoomout.setEnabled(true);
                        }
                        m_jTfZoom.setText(m_nZoomPercents[i + 1] + "%");
                        m_fScale = m_nZoomPercents[i + 1] / 100D;
                  } else {
                        if (!a_zoomout.isEnabled())
                              a_zoomout.setEnabled(true);
                        m_jTfZoom.setText(m_nZoomPercents[0] + "%");
                        m_fScale = m_nZoomPercents[0] / 100D;
                  }
                  setAppropriateSize();
                  m_GraphPanel.repaint();
                  m_GraphPanel.invalidate();
                  m_jScrollPane.revalidate();
                m_jStatusBar.setText("Zooming in");
            }
      } // class ActionZoomIn

      class ActionZoomOut extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -203891108593551L;

            public ActionZoomOut() {
                  super("Zoom out", "Zoom out", "zoomout", "-");
            } // c'tor

            public void actionPerformed(ActionEvent ae) {
                  int i = 0, s = (int) (m_fScale * 100);
                  if (s < 300)
                        i = (int) Math.ceil(s / 25D);
                  else if (s < 700)
                        i = 6 + (int) Math.ceil(s / 50D);
                  else
                        i = 13 + (int) Math.ceil(s / 100D);

                  if (s <= 10) {
                        setEnabled(false);
                  } else if (s < 999) {
                        if (i <= 1) {
                              setEnabled(false);
                        }
                        m_jTfZoom.setText(m_nZoomPercents[i - 1] + "%");
                        m_fScale = m_nZoomPercents[i - 1] / 100D;
                  } else {
                        if (!a_zoomin.isEnabled())
                              a_zoomin.setEnabled(true);
                        m_jTfZoom.setText(m_nZoomPercents[22] + "%");
                        m_fScale = m_nZoomPercents[22] / 100D;
                  }
                  setAppropriateSize();
                  m_GraphPanel.repaint();
                  m_GraphPanel.invalidate();
                  m_jScrollPane.revalidate();
                m_jStatusBar.setText("Zooming out");
            }
      } // class ActionZoomOut

      class ActionLayout extends MyAction {
            /** for serialization */
            private static final long serialVersionUID = -203891108593551L;

            public ActionLayout() {
                  super("Layout", "Layout Graph", "layout", "ctrl L");
            } // c'tor

            JDialog dlg = null;

            public void actionPerformed(ActionEvent ae) {
                  if (dlg == null) {
                        dlg = new JDialog();
                        dlg.setTitle("Graph Layout Options");
                        final JCheckBox jCbCustomNodeSize = new JCheckBox("Custom Node Size");
                        final JLabel jLbNodeWidth = new JLabel("Width");
                        final JLabel jLbNodeHeight = new JLabel("Height");

                        m_jTfNodeWidth.setHorizontalAlignment(JTextField.CENTER);
                        m_jTfNodeWidth.setText("" + m_nNodeWidth);
                        m_jTfNodeHeight.setHorizontalAlignment(JTextField.CENTER);
                        m_jTfNodeHeight.setText("" + m_nNodeHeight);
                        jLbNodeWidth.setEnabled(false);
                        m_jTfNodeWidth.setEnabled(false);
                        jLbNodeHeight.setEnabled(false);
                        m_jTfNodeHeight.setEnabled(false);

                        jCbCustomNodeSize.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    if (((JCheckBox) ae.getSource()).isSelected()) {
                                          jLbNodeWidth.setEnabled(true);
                                          m_jTfNodeWidth.setEnabled(true);
                                          jLbNodeHeight.setEnabled(true);
                                          m_jTfNodeHeight.setEnabled(true);
                                    } else {
                                          jLbNodeWidth.setEnabled(false);
                                          m_jTfNodeWidth.setEnabled(false);
                                          jLbNodeHeight.setEnabled(false);
                                          m_jTfNodeHeight.setEnabled(false);
                                          setAppropriateSize();
                                          setAppropriateNodeSize();
                                    }
                              }
                        });
                        JButton jBtLayout;
                        jBtLayout = new JButton("Layout Graph");
                        jBtLayout.setMnemonic('L');

                        jBtLayout.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    int tmpW, tmpH;

                                    if (jCbCustomNodeSize.isSelected()) {
                                          try {
                                                tmpW = Integer.parseInt(m_jTfNodeWidth.getText());
                                          } catch (NumberFormatException ne) {
                                                JOptionPane.showMessageDialog(GUI.this.getParent(),
                                                            "Invalid integer entered for node width.", "Error", JOptionPane.ERROR_MESSAGE);
                                                tmpW = m_nNodeWidth;
                                                m_jTfNodeWidth.setText("" + m_nNodeWidth);

                                          }
                                          try {
                                                tmpH = Integer.parseInt(m_jTfNodeHeight.getText());
                                          } catch (NumberFormatException ne) {
                                                JOptionPane.showMessageDialog(GUI.this.getParent(),
                                                            "Invalid integer entered for node height.", "Error", JOptionPane.ERROR_MESSAGE);
                                                tmpH = m_nNodeHeight;
                                                m_jTfNodeWidth.setText("" + m_nNodeHeight);
                                          }

                                          if (tmpW != m_nNodeWidth || tmpH != m_nNodeHeight) {
                                                m_nNodeWidth = tmpW;
                                                m_nPaddedNodeWidth = m_nNodeWidth + PADDING;
                                                m_nNodeHeight = tmpH;
                                          }
                                    }
                                    // JButton bt = (JButton) ae.getSource();
                                    // bt.setEnabled(false);
                                    dlg.setVisible(false);
                                    updateStatus();
                                    layoutGraph();
                                  m_jStatusBar.setText("Laying out Bayes net");
                              }
                        });
                        JButton jBtCancel;
                        jBtCancel = new JButton("Cancel");
                        jBtCancel.setMnemonic('C');
                        jBtCancel.addActionListener(new ActionListener() {
                              public void actionPerformed(ActionEvent ae) {
                                    dlg.setVisible(false);
                              }
                        });
                        GridBagConstraints gbc = new GridBagConstraints();
                        dlg.setLayout(new GridBagLayout());
                        //dlg.add(m_le.getControlPanel());

                        Container c = new Container();
                        c.setLayout(new GridBagLayout());

                        gbc.gridwidth = 1;
                        gbc.insets = new Insets(8, 0, 0, 0);
                        gbc.anchor = GridBagConstraints.NORTHWEST;
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(jCbCustomNodeSize, gbc);
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        c.add(jLbNodeWidth, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(m_jTfNodeWidth, gbc);
                        gbc.gridwidth = GridBagConstraints.RELATIVE;
                        c.add(jLbNodeHeight, gbc);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        c.add(m_jTfNodeHeight, gbc);
                        gbc.fill = GridBagConstraints.HORIZONTAL;
                        dlg.add(c, gbc);
                        dlg.add(jBtLayout);
                        gbc.gridwidth = GridBagConstraints.REMAINDER;
                        dlg.add(jBtCancel);
                  }
                  dlg.setLocation(100, 100);
                  dlg.setVisible(true);
                  dlg.setSize(dlg.getPreferredSize());
                  dlg.setVisible(false);
                  dlg.setVisible(true);
                  dlg.repaint();
            }
      } // class ActionLayout

      /**
       * Constructor<br>
       * Sets up the gui and initializes all the other previously uninitialized
       * variables.
       */
01786       public GUI() {
            m_GraphPanel = new GraphPanel();
            m_jScrollPane = new JScrollPane(m_GraphPanel);

            // creating a new layout engine and adding this class as its listener
            // to receive layoutComplete events
      
            m_jTfZoom = new JTextField("100%");
            m_jTfZoom.setMinimumSize(m_jTfZoom.getPreferredSize());
            m_jTfZoom.setHorizontalAlignment(JTextField.CENTER);
            m_jTfZoom.setToolTipText("Zoom");

            m_jTfZoom.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent ae) {
                        JTextField jt = (JTextField) ae.getSource();
                        try {
                              int i = -1;
                              i = jt.getText().indexOf('%');
                              if (i == -1)
                                    i = Integer.parseInt(jt.getText());
                              else
                                    i = Integer.parseInt(jt.getText().substring(0, i));

                              if (i <= 999)
                                    m_fScale = i / 100D;

                              jt.setText((int) (m_fScale * 100) + "%");
                              if (m_fScale > 0.1) {
                                    if (!a_zoomout.isEnabled())
                                          a_zoomout.setEnabled(true);
                              } else
                                    a_zoomout.setEnabled(false);
                              if (m_fScale < 9.99) {
                                    if (!a_zoomin.isEnabled())
                                          a_zoomin.setEnabled(true);
                              } else
                                    a_zoomin.setEnabled(false);
                              setAppropriateSize();
                              // m_GraphPanel.clearBuffer();
                              m_GraphPanel.repaint();
                              m_GraphPanel.invalidate();
                              m_jScrollPane.revalidate();
                        } catch (NumberFormatException ne) {
                              JOptionPane.showMessageDialog(GUI.this.getParent(),
                                          "Invalid integer entered for zoom.", "Error", JOptionPane.ERROR_MESSAGE);
                              jt.setText((m_fScale * 100) + "%");
                        }
                  }
            });

            GridBagConstraints gbc = new GridBagConstraints();

            final JPanel p = new JPanel(new GridBagLayout());
            p.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createTitledBorder("ExtraControls"), BorderFactory
                        .createEmptyBorder(4, 4, 4, 4)));
            p.setPreferredSize(new Dimension(0, 0));

            m_jTbTools = new JToolBar();
            m_jTbTools.setFloatable(false);
            m_jTbTools.setLayout(new GridBagLayout());
            gbc.anchor = GridBagConstraints.NORTHWEST;
            gbc.gridwidth = GridBagConstraints.REMAINDER;
            gbc.insets = new Insets(0, 0, 0, 0);
            m_jTbTools.add(p, gbc);
            gbc.gridwidth = 1;


            m_jTbTools.add(a_new);
            m_jTbTools.add(a_save);
            m_jTbTools.add(a_load);
            m_jTbTools.addSeparator(new Dimension(2, 2));
            m_jTbTools.add(a_cutnode);
            m_jTbTools.add(a_copynode);
            m_jTbTools.add(a_pastenode);
            m_jTbTools.addSeparator(new Dimension(2, 2));
            m_jTbTools.add(a_undo);
            m_jTbTools.add(a_redo);
            m_jTbTools.addSeparator(new Dimension(2, 2));
            m_jTbTools.add(a_alignleft);
            m_jTbTools.add(a_alignright);
            m_jTbTools.add(a_aligntop);
            m_jTbTools.add(a_alignbottom);
            m_jTbTools.add(a_centerhorizontal);
            m_jTbTools.add(a_centervertical);
            m_jTbTools.add(a_spacehorizontal);
            m_jTbTools.add(a_spacevertical);

            
            m_jTbTools.addSeparator(new Dimension(2, 2));
            m_jTbTools.add(a_zoomin);

            gbc.fill = GridBagConstraints.VERTICAL;
            gbc.weighty = 1;
            JPanel p2 = new JPanel(new BorderLayout());
            p2.setPreferredSize(m_jTfZoom.getPreferredSize());
            p2.setMinimumSize(m_jTfZoom.getPreferredSize());
            p2.add(m_jTfZoom, BorderLayout.CENTER);
            m_jTbTools.add(p2, gbc);
            gbc.weighty = 0;
            gbc.fill = GridBagConstraints.NONE;

            m_jTbTools.add(a_zoomout);
            m_jTbTools.addSeparator(new Dimension(2, 2));

            // jTbTools.add(jBtExtraControls, gbc);
            m_jTbTools.add(a_layout);
            m_jTbTools.addSeparator(new Dimension(4, 2));
            gbc.weightx = 1;
            gbc.fill = GridBagConstraints.BOTH;
            //jTbTools.add(m_layoutEngine.getProgressBar(), gbc);
            m_jStatusBar = new JLabel("Status bar");

            this.setLayout(new BorderLayout());
            this.add(m_jTbTools, BorderLayout.NORTH);
            this.add(m_jScrollPane, BorderLayout.CENTER);
            this.add(m_jStatusBar, BorderLayout.SOUTH);

            updateStatus();
            a_datagenerator.setEnabled(false);
      }

      /**
       * This method sets the node size that is appropriate considering the
       * maximum label size that is present. It is used internally when custom
       * node size checkbox is unchecked.
       */
01912       protected void setAppropriateNodeSize() {
            int strWidth;
            FontMetrics fm = this.getFontMetrics(this.getFont());
            int nMaxStringWidth = DEFAULT_NODE_WIDTH;
            if (nMaxStringWidth == 0)
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        strWidth = fm.stringWidth(m_BayesNet.getNodeName(iNode));
                        if (strWidth > nMaxStringWidth)
                              nMaxStringWidth = strWidth;
                  }
            m_nNodeWidth = nMaxStringWidth + 4;
            m_nPaddedNodeWidth = m_nNodeWidth + PADDING;
            m_jTfNodeWidth.setText("" + m_nNodeWidth);

            m_nNodeHeight = 2 * fm.getHeight();
            m_jTfNodeHeight.setText("" + m_nNodeHeight);
      }

      /**
       * Sets the preferred size for m_GraphPanel GraphPanel to the minimum size that is
       * neccessary to display the graph.
       */
01934       public void setAppropriateSize() {
            int maxX = 0, maxY = 0;

            m_GraphPanel.setScale(m_fScale, m_fScale);

            for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                  int nPosX = m_BayesNet.getPositionX(iNode); 
                  int nPosY = m_BayesNet.getPositionY(iNode); 
                  if (maxX < nPosX)
                        maxX = nPosX + 100;
                  if (maxY < nPosY)
                        maxY = nPosY;
            }
            m_GraphPanel.setPreferredSize(new Dimension((int) ((maxX + m_nPaddedNodeWidth + 2) * m_fScale),
                        (int) ((maxY + m_nNodeHeight + 2) * m_fScale)));
            m_GraphPanel.revalidate();
      } // setAppropriateSize

      /**
       * This method is an implementation for LayoutCompleteEventListener class.
       * It sets the size appropriate for m_GraphPanel GraphPanel and and revalidates it's
       * container JScrollPane once a LayoutCompleteEvent is received from the
       * LayoutEngine. Also, it updates positions of the Bayesian network stored
       * in m_BayesNet.
       */
01959       public void layoutCompleted(LayoutCompleteEvent le) {
            LayoutEngine layoutEngine  = m_layoutEngine; // (LayoutEngine) le.getSource();
            FastVector nPosX = new FastVector(m_BayesNet.getNrOfNodes());
            FastVector nPosY = new FastVector(m_BayesNet.getNrOfNodes());
            for (int iNode = 0; iNode < layoutEngine.getNodes().size(); iNode++) {
                  GraphNode gNode = (GraphNode) layoutEngine.getNodes().elementAt(iNode);
                  if (gNode.nodeType == GraphNode.NORMAL) {
                        nPosX.addElement(gNode.x);
                        nPosY.addElement(gNode.y);
                  }
            }
            m_BayesNet.layoutGraph(nPosX, nPosY);
            m_jStatusBar.setText("Graph layed out");
            a_undo.setEnabled(true);
            a_redo.setEnabled(false);
            setAppropriateSize();
            m_GraphPanel.invalidate();
            m_jScrollPane.revalidate();
            m_GraphPanel.repaint();
      } // layoutCompleted


      /**
       * BIF reader<br>
       * Reads a graph description in XMLBIF03 from an file
       * with name sFileName
       */
01986       public void readBIFFromFile(String sFileName) throws BIFFormatException, IOException {
            m_sFileName = sFileName;
            try {

                  BIFReader bayesNet = new BIFReader();
                  bayesNet.processFile(sFileName);
                  m_BayesNet = new EditableBayesNet(bayesNet);
                  updateStatus();
                  a_datagenerator.setEnabled(m_BayesNet.getNrOfNodes() > 0);
                  m_BayesNet.clearUndoStack();
            } catch (Exception ex) {
                  ex.printStackTrace();
                  return;
            }

            setAppropriateNodeSize();
            setAppropriateSize();
      } // readBIFFromFile

      /* read arff file from file sFileName 
       * and start new Bayesian network with nodes 
       * representing attributes in data set.
       */
      void initFromArffFile(String sFileName) {
            try {
                  Instances instances = new Instances(new FileReader(sFileName));
                  m_BayesNet = new EditableBayesNet(instances);
                  m_Instances = instances;
                  a_learn.setEnabled(true);
                  a_learnCPT.setEnabled(true);
                  setAppropriateNodeSize();
                  setAppropriateSize();
            } catch (Exception ex) {
                  ex.printStackTrace();
                  return;
            }
      } // initFromArffFile
      
      /**
       * The panel which contains the actual Bayeian network.
       */
02027       private class GraphPanel extends PrintablePanel implements Printable {

            /** for serialization */
02030             private static final long serialVersionUID = -3562813603236753173L;

            /** node drawing modes */
02033             final static int HIGHLIGHTED = 1;
            final static int NORMAL = 0;

            public GraphPanel() {
                  super();
                  this.addMouseListener(new GraphVisualizerMouseListener());
                  this.addMouseMotionListener(new GraphVisualizerMouseMotionListener());
                  this.setToolTipText("");
            } // c'tor

            /* For showing instructions when hovering over a node
             *  (non-Javadoc)
             * @see javax.swing.JComponent#getToolTipText(java.awt.event.MouseEvent)
             */
            public String getToolTipText(MouseEvent me) {
                  int x, y;
                  Rectangle r;
                  x = y  = 0;

                  r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale), (int) (m_nNodeHeight * m_fScale));
                  x += me.getX();
                  y += me.getY();
                  
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale);
                        r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale);
                        if (r.contains(x, y)) {
                              return m_BayesNet.getNodeName(iNode) + " (right click to manipulate this node)";
                        }
                  }
                  return null;
            } // getToolTipText

            /* Code for showing the graph in the panel.
             *  (non-Javadoc)
             * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
             */
            public void paintComponent(Graphics gr) {
                  Graphics2D g = (Graphics2D) gr;
                  RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                  rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
                  g.setRenderingHints(rh);
                  g.scale(m_fScale, m_fScale);
                  Rectangle r = g.getClipBounds();
                  g.clearRect(r.x, r.y, r.width, r.height);

                  if (m_bViewCliques) {
                        m_nClique = 1;
                        viewCliques(g, m_marginCalculator.m_root);
                  }                 
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        drawNode(g, iNode, NORMAL);
                  }
                  if (!a_export.isExporting() && !a_print.isPrinting()) {
                        m_Selection.draw(g);
                  }
                  if (m_nSelectedRect != null) {
                        g.drawRect((int)(m_nSelectedRect.x/ m_fScale), 
                                    (int)(m_nSelectedRect.y/ m_fScale), 
                                    (int)(m_nSelectedRect.width/ m_fScale), 
                                    (int)(m_nSelectedRect.height/ m_fScale));
                  }
            } // paintComponent

            /** number of the clique being drawn. Used for selecting the color of the clique */
02098             int m_nClique = 1;

            /* draws cliques in junction tree. 
             * 
             */
            void viewCliques(Graphics g, JunctionTreeNode node) {
                              int [] nodes = node.m_nNodes;
                              g.setColor(
                                          new Color(m_nClique % 7 * 256 /7, 
                                          (m_nClique % 2 * 256 / 2),
                                          (m_nClique % 3 * 256 / 3))
                                          );
                              int dX = m_nPaddedNodeWidth / 2 + m_nClique;
                              int dY = m_nNodeHeight / 2;
                              int nPosX = 0;
                              int nPosY = 0;
                              String sStr = "";
                              for (int j = 0; j < nodes.length; j++) {
                                    nPosX += m_BayesNet.getPositionX(nodes[j]);
                                    nPosY += m_BayesNet.getPositionY(nodes[j]);
                                    sStr += " " + nodes[j];
                                    for (int k = j+1; k < nodes.length; k++) {
                                          g.drawLine(
                                                      m_BayesNet.getPositionX(nodes[j]) + dX,
                                                      m_BayesNet.getPositionY(nodes[j]) + dY,
                                                      m_BayesNet.getPositionX(nodes[k]) + dX,
                                                      m_BayesNet.getPositionY(nodes[k]) + dY
                                                      );
                                    }
                              }
                              m_nClique++;
                              nPosX /= nodes.length;
                              nPosY /= nodes.length;
                              g.drawString("Clique " + m_nClique + "("+sStr+")", nPosX, nPosY);
                              for (int iChild = 0; iChild < node.m_children.size(); iChild++) {
                                    viewCliques(g, (JunctionTreeNode) node.m_children.elementAt(iChild));
                              }
            } // viewCliques
            
            
            /* Draw a node with index iNode on Graphics g at position
             * Drawing mode can be NORMAL or HIGHLIGHTED.
             */
            protected void drawNode(Graphics g, int iNode, int mode) {
                  int nPosX = m_BayesNet.getPositionX(iNode);
                  int nPosY = m_BayesNet.getPositionY(iNode);
                  g.setColor(this.getBackground().darker().darker());
                  FontMetrics fm = getFontMetrics(getFont());
                  
                  if (mode == HIGHLIGHTED) {
                        g.setXORMode(Color.green); // g.setColor(Color.green);
                  }
                  g.fillOval(nPosX + m_nPaddedNodeWidth - m_nNodeWidth - (m_nPaddedNodeWidth - m_nNodeWidth) / 2, nPosY,
                              m_nNodeWidth, m_nNodeHeight);
                  g.setColor(Color.white);
                  if (mode == HIGHLIGHTED) {
                        g.setXORMode(Color.red);
                  }

                  // Draw the node's label if it can fit inside the node's
                  // current width otherwise just display its node nr
                  // if it can fit in node's current width
                  if (fm.stringWidth(m_BayesNet.getNodeName(iNode)) <= m_nNodeWidth) {
                        g.drawString(m_BayesNet.getNodeName(iNode), nPosX + m_nPaddedNodeWidth / 2
                                    - fm.stringWidth(m_BayesNet.getNodeName(iNode)) / 2, nPosY + m_nNodeHeight / 2
                                    + fm.getHeight() / 2 - 2);
                  } else if (fm.stringWidth("" + iNode) <= m_nNodeWidth) {
                        g.drawString("" + iNode, nPosX + m_nPaddedNodeWidth / 2 - fm.stringWidth("" + iNode) / 2, 
                                    nPosY + m_nNodeHeight / 2 + fm.getHeight() / 2 - 2);
                  }

                  if (mode == HIGHLIGHTED) {
                        g.setXORMode(Color.green);
                  }

                  if (m_bViewMargins) {
                        if (m_BayesNet.getEvidence(iNode) < 0) {
                              g.setColor(new Color(0, 128, 0));
                        } else {
                              g.setColor(new Color(128, 0, 0));
                        }
                        double[] P = m_BayesNet.getMargin(iNode);
                        for (int iValue = 0; iValue < P.length; iValue++) {
                              String sP = P[iValue] + "";
                              if (sP.charAt(0) == '0') {
                                    sP = sP.substring(1);
                              }
                              if (sP.length() > 5) {
                                    sP = sP.substring(1, 5);
                              }
                              g.fillRect(nPosX + m_nPaddedNodeWidth, nPosY + iValue * 10 + 2, (int) (P[iValue] * 100), 8);
                              g.drawString(m_BayesNet.getNodeValue(iNode, iValue) + " " + sP, nPosX + m_nPaddedNodeWidth
                                          + (int) (P[iValue] * 100), nPosY + iValue * 10 + 10);

                        }
                  }
                  if (m_bViewCliques) {
                              return;
                  }
                  g.setColor(Color.black);
                  // Drawing all incoming edges into the node,
                  for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
                        int nParent = m_BayesNet.getParent(iNode, iParent);
                        int nPosX1 = nPosX + m_nPaddedNodeWidth / 2;
                        int nPosY1 = nPosY + m_nNodeHeight;
                        int nPosX2 = m_BayesNet.getPositionX(nParent);
                        int nPosY2 = m_BayesNet.getPositionY(nParent);
                        int nPosX2b = nPosX2 + m_nPaddedNodeWidth / 2;
                        int nPosY2b = nPosY2;

                        double phi = Math.atan2((nPosX2b - nPosX1 + 0.0) * m_nNodeHeight, (nPosY2b - nPosY1 + 0.0) * m_nNodeWidth);
                        nPosX1 = (int) (nPosX + m_nPaddedNodeWidth / 2 + Math.sin(phi) * m_nNodeWidth / 2);
                        nPosY1 = (int) (nPosY + m_nNodeHeight / 2 + Math.cos(phi) * m_nNodeHeight / 2);
                        nPosX2b = (int) (nPosX2 + m_nPaddedNodeWidth / 2 - Math.sin(phi) * m_nNodeWidth / 2);
                        nPosY2b = (int) (nPosY2 + m_nNodeHeight / 2 - Math.cos(phi) * m_nNodeHeight / 2);
                        drawArrow(g, nPosX2b, nPosY2b, nPosX1, nPosY1);
                  }
                  if (mode == HIGHLIGHTED) {
                  FastVector children = m_BayesNet.getChildren(iNode);
                  for (int iChild = 0; iChild < children.size(); iChild++) {
                        int nChild = (Integer) children.elementAt(iChild);
                        int nPosX1 = nPosX + m_nPaddedNodeWidth / 2;
                        int nPosY1 = nPosY;
                        int nPosX2 = m_BayesNet.getPositionX(nChild);
                        int nPosY2 = m_BayesNet.getPositionY(nChild);
                        int nPosX2b = nPosX2 + m_nPaddedNodeWidth / 2;
                        int nPosY2b = nPosY2 + m_nNodeHeight;

                        double phi = Math.atan2((nPosX2b - nPosX1 + 0.0) * m_nNodeHeight, (nPosY2b - nPosY1 + 0.0) * m_nNodeWidth);
                        nPosX1 = (int) (nPosX + m_nPaddedNodeWidth / 2 + Math.sin(phi) * m_nNodeWidth / 2);
                        nPosY1 = (int) (nPosY + m_nNodeHeight / 2 + Math.cos(phi) * m_nNodeHeight / 2);
                        nPosX2b = (int) (nPosX2 + m_nPaddedNodeWidth / 2 - Math.sin(phi) * m_nNodeWidth / 2);
                        nPosY2b = (int) (nPosY2 + m_nNodeHeight / 2 - Math.cos(phi) * m_nNodeHeight / 2);
                        drawArrow(g, nPosX1, nPosY1, nPosX2b, nPosY2b);
                  }
                  }
            } // drawNode

            
            /**
             * This method draws an arrow on a line from (x1,y1) to (x2,y2). The
             * arrow head is seated on (x2,y2) and is in the direction of the line.
             * If the arrow is needed to be drawn in the opposite direction then
             * simply swap the order of (x1, y1) and (x2, y2) when calling this
             * function.
             */
02244             protected void drawArrow(Graphics g, int nPosX1, int nPosY1, int nPosX2, int nPosY2) {
                  g.drawLine(nPosX1, nPosY1, nPosX2, nPosY2);

                  if (nPosX1 == nPosX2) {
                        if (nPosY1 < nPosY2) {
                              g.drawLine(nPosX2, nPosY2, nPosX2 + 4, nPosY2 - 8);
                              g.drawLine(nPosX2, nPosY2, nPosX2 - 4, nPosY2 - 8);
                        } else {
                              g.drawLine(nPosX2, nPosY2, nPosX2 + 4, nPosY2 + 8);
                              g.drawLine(nPosX2, nPosY2, nPosX2 - 4, nPosY2 + 8);
                        }
                  } else {
                        // theta=line's angle from base, beta=angle of arrow's side from
                        // line
                        double hyp = 0, base = 0, perp = 0, theta, beta;
                        int nPosX3 = 0, nPosY3 = 0;

                        if (nPosX2 < nPosX1) {
                              base = nPosX1 - nPosX2;
                              hyp = Math.sqrt((nPosX2 - nPosX1) * (nPosX2 - nPosX1) + (nPosY2 - nPosY1) * (nPosY2 - nPosY1));
                              theta = Math.acos(base / hyp);
                        } else { // x1>x2 as we already checked x1==x2 before
                              base = nPosX1 - nPosX2;
                              hyp = Math.sqrt((nPosX2 - nPosX1) * (nPosX2 - nPosX1) + (nPosY2 - nPosY1) * (nPosY2 - nPosY1));
                              theta = Math.acos(base / hyp);
                        }
                        beta = 30 * Math.PI / 180;

                        hyp = 8;
                        base = Math.cos(theta - beta) * hyp;
                        perp = Math.sin(theta - beta) * hyp;

                        nPosX3 = (int) (nPosX2 + base);
                        if (nPosY1 < nPosY2)
                              nPosY3 = (int) (nPosY2 - perp);
                        else
                              nPosY3 = (int) (nPosY2 + perp);

                        g.drawLine(nPosX2, nPosY2, nPosX3, nPosY3);

                        base = Math.cos(theta + beta) * hyp;
                        perp = Math.sin(theta + beta) * hyp;

                        nPosX3 = (int) (nPosX2 + base);
                        if (nPosY1 < nPosY2)
                              nPosY3 = (int) (nPosY2 - perp);
                        else
                              nPosY3 = (int) (nPosY2 + perp);
                        g.drawLine(nPosX2, nPosY2, nPosX3, nPosY3);
                  }
            } // drawArrow

            /**
             * This method highlights a given node and all its incoming and outgoing arcs
             */
02299             public void highLight(int iNode) {
                  Graphics2D g = (Graphics2D) this.getGraphics();
                  RenderingHints rh = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
                  rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
                  g.setRenderingHints(rh);
                  g.setPaintMode();
                  g.scale(m_fScale, m_fScale);
                  drawNode(g, iNode, HIGHLIGHTED);
            } // highlight

          /** implementation of Printable, used for printing
           * @see Printable
           */
02312             public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
                if (pageIndex > 0) {
                  return(NO_SUCH_PAGE);
                } else {
                  Graphics2D g2d = (Graphics2D)g;
                  g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
                  double fHeight = pageFormat.getImageableHeight();
                  double fWidth = pageFormat.getImageableWidth();
                  int xMax = 1;
                  int yMax = 1;
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        if (xMax < m_BayesNet.getPositionX(iNode)) {
                              xMax = m_BayesNet.getPositionX(iNode);
                        }
                        if (yMax < m_BayesNet.getPositionY(iNode)) {
                              yMax = m_BayesNet.getPositionY(iNode);
                        }
                  }
                  double fCurrentScale = m_fScale;
                  xMax += m_nPaddedNodeWidth + 100;
                  if (fWidth/xMax < fHeight/yMax) {
                        m_fScale = fWidth/xMax; 
                  } else {
                        m_fScale = fHeight/yMax;
                  }
                  
                  // Turn off double buffering
                  paint(g2d);
                  m_fScale = fCurrentScale;
                  // Turn double buffering back on
                  return(PAGE_EXISTS);
                }
              } // print
                        
      } // class GraphPanel

      /**
       * Table Model for the Table for editing CPTs
       */
02351       private class GraphVisualizerTableModel extends AbstractTableModel {

            /** for serialization */
02354             private static final long serialVersionUID = -4789813491347366596L;
            /** labels for the columns */
02356             final String [] m_sColumnNames;
            /** probability table data **/
02358             final double [][] m_fProbs;
            /** nr of node for currently editted CPT */
02360             int m_iNode;

            public GraphVisualizerTableModel(int iNode) {
                  m_iNode = iNode;
                  double [][] probs = m_BayesNet.getDistribution(iNode);
                  m_fProbs = new double[probs.length][probs[0].length];
                  for (int i = 0; i < probs.length; i++) {
                        for (int j = 0; j < probs[0].length; j++) {
                              m_fProbs[i][j] = probs[i][j];
                        }
                  }
                  m_sColumnNames = m_BayesNet.getValues(iNode);
            } // c'tor

            /** method that generates random CPTs
             */
02376             public void randomize() {
                  int nProbs = m_fProbs[0].length;
                  Random random = new Random();
                  for (int i = 0; i < m_fProbs.length; i++) {
                        // get random nrs
                        for (int j = 0; j < nProbs-1; j++) {
                              m_fProbs[i][j] = random.nextDouble();
                        }
                        // sort
                        for (int j = 0; j < nProbs-1; j++) {
                              for (int k = j+1; k < nProbs-1; k++) {
                                    if (m_fProbs[i][j] > m_fProbs[i][k]) {
                                          double h = m_fProbs[i][j]; 
                                          m_fProbs[i][j] = m_fProbs[i][k];
                                          m_fProbs[i][k] = h;
                                    }
                              }
                        }
                        double sum = m_fProbs[i][0];
                        for (int j = 1; j < nProbs-1; j++) {
                              m_fProbs[i][j] = m_fProbs[i][j] - sum;
                              sum += m_fProbs[i][j]; 
                        }
                        m_fProbs[i][nProbs - 1] = 1.0 - sum;
                  }
            } // randomize
            
            public void setData() {}

            /** return nr of colums */
02406             public int getColumnCount() {
                  return m_sColumnNames.length;
            }

            /** return nr of rows */
02411             public int getRowCount() {
                  return m_fProbs.length;
            }

            /** return name of specified colum 
             * @param iCol index of the column
             */
02418             public String getColumnName(int iCol) {
                  return m_sColumnNames[iCol];
            }
            /** return data point 
             * @param iRow index of row in table
             * @param iCol index of column in table
             */
02425             public Object getValueAt(int iRow, int iCol) {
                  return new Double(m_fProbs[iRow][iCol]);
            }

            /** Set data point, assigns value to CPT entry
             * specified by row and column. The remainder of the
             * CPT is normalized so that the values add up to 1.
             * IF a value below zero of over 1 is given, no changes
             * take place.
             * @param oProb data point
             * @param iRow index of row in table
             * @param iCol index of column in table
             */
02438             public void setValueAt(Object oProb, int iRow, int iCol) {
                  Double fProb = (Double) oProb;
                  if (fProb < 0 || fProb > 1) {
                        return;
                  }
                  m_fProbs[iRow][iCol] = (double) fProb;
                  double sum = 0;
                  for (int i = 0; i < m_fProbs[iRow].length; i++) {
                        sum += m_fProbs[iRow][i];
                  }

                  if (sum > 1) {
                        // handle overflow
                        int i = m_fProbs[iRow].length - 1;
                        while (sum > 1) {
                              if (i != iCol) {
                                    if (m_fProbs[iRow][i] > sum - 1) {
                                          m_fProbs[iRow][i] -= sum - 1;
                                          sum = 1;
                                    } else {
                                          sum -= m_fProbs[iRow][i];
                                          m_fProbs[iRow][i] = 0;
                                    }
                              }
                              i--;
                        }
                  } else {
                        // handle underflow
                        int i = m_fProbs[iRow].length - 1;
                        while (sum < 1) {
                              if (i != iCol) {
                                    m_fProbs[iRow][i] += 1 - sum;
                                    sum = 1;
                              }
                              i--;
                        }

                  }
                  validate();
            } // setData

            /*
             * JTable uses this method to determine the default renderer/ editor for
             * each cell.
             */
            public Class getColumnClass(int c) {
                  return getValueAt(0, c).getClass();
            }

            /*
             * Implemented this to make sure the table is uneditable.
             */
            public boolean isCellEditable(int row, int col) {
                  return true;
            }
      } // class GraphVisualizerTableModel

      /**
       * Listener class for processing mouseClicked
       */
02498       private class GraphVisualizerMouseListener extends MouseAdapter {

            /** A left mouseclick on a node adds node to selection (depending
             * on shift and ctrl keys).
             * A right mouseclick on a node pops up menu with actions to be
             * performed on the node.
             * A right mouseclick outside another node pops up menu.
             */
02506             public void mouseClicked(MouseEvent me) {
                  int x, y;

                  Rectangle r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale), (int) (m_nNodeHeight * m_fScale));
                  x = me.getX();
                  y = me.getY();

                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale);
                        r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale);
                        if (r.contains(x, y)) {
                              m_nCurrentNode = iNode;                         
                              if (me.getButton() == MouseEvent.BUTTON3) {
                                    handleRightNodeClick(me);
                              }
                              if (me.getButton() == MouseEvent.BUTTON1) {
                                    if ((me.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
                                          m_Selection.toggleSelection(m_nCurrentNode);
                                    } else if ((me.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
                                          m_Selection.addToSelection(m_nCurrentNode);
                                    } else {
                                          m_Selection.clear();
                                          m_Selection.addToSelection(m_nCurrentNode);
                                    }
                                    repaint();
                              }
                              return;
                        }
                  }
                  if (me.getButton() == MouseEvent.BUTTON3) {
                        handleRightClick(me, (int)(x/m_fScale), (int)(y/m_fScale));
                  }
            } // mouseClicked
            
            /* update selection
             *  (non-Javadoc)
             * @see java.awt.event.MouseListener#mouseReleased(java.awt.event.MouseEvent)
             */
            public void mouseReleased(MouseEvent me) {
            if (m_nSelectedRect != null) {
                        if ((me.getModifiersEx() & MouseEvent.CTRL_DOWN_MASK) != 0) {
                              m_Selection.toggleSelection(m_nSelectedRect);
                        } else if ((me.getModifiersEx() & MouseEvent.SHIFT_DOWN_MASK) != 0) {
                              m_Selection.addToSelection(m_nSelectedRect);
                        } else {
                              m_Selection.clear();
                        m_Selection.addToSelection(m_nSelectedRect);
                        }
                  m_nSelectedRect = null;
                  repaint();
            }
          } // mouseReleased

            /** position clicked on */
02560           int m_nPosX = 0, m_nPosY = 0;
          
          /* pop up menu with actions that apply in general or to selection (if any exists)
           */
          void handleRightClick(MouseEvent me, int nPosX, int nPosY) {
      
                  ActionListener act = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              if (ae.getActionCommand().equals("Add node")) {
                                    a_addnode.addNode(m_nPosX, m_nPosY);
                                    return;
                              }
                              repaint();
                        }
                  };
                  JPopupMenu popupMenu = new JPopupMenu("Choose a value");

                  JMenuItem addNodeItem = new JMenuItem("Add node");
                  addNodeItem.addActionListener(act);
                  popupMenu.add(addNodeItem);

                  FastVector selected = m_Selection.getSelected();
                  JMenu addArcMenu = new JMenu("Add parent");
                  popupMenu.add(addArcMenu);
                  if (selected.size() == 0) {
                        addArcMenu.setEnabled(false);
                  } else {
                  int nNodes = m_BayesNet.getNrOfNodes();
                  boolean[] isNotAllowedAsParent = new boolean[nNodes];
                  // prevent it being a parent of itself
                  for (int iNode = 0; iNode < selected.size(); iNode++) {
                        isNotAllowedAsParent[(Integer) selected.elementAt(iNode)] = true;
                  }
                  // prevent a descendant being a parent, since it introduces cycles
                  for (int i = 0; i < nNodes; i++) {
                        for (int iNode = 0; iNode < nNodes; iNode++) {
                              for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
                                    if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) {
                                          isNotAllowedAsParent[iNode] = true;
                                    }
                              }
                        }
                  }
                  // prevent nodes that are already a parent
                  for (int iNode = 0; iNode < selected.size(); iNode++) {
                        int nNode = (Integer) selected.elementAt(iNode);
                        for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(nNode); iParent++) {
                              isNotAllowedAsParent[m_BayesNet.getParent(nNode, iParent)] = true;
                        }
                  }
                  ActionListener addParentAction = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              try {
                                    m_BayesNet.addArc(ae.getActionCommand(), m_Selection.getSelected());
                                    m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                                    updateStatus();
                              } catch (Exception e) {
                                    e.printStackTrace();
                              }
                        }
                  };
                  // count nr of remaining candidates
                  int nCandidates = 0;
                  for (int i = 0; i < nNodes; i++) {
                        if (!isNotAllowedAsParent[i]) {
                              JMenuItem item = new JMenuItem(m_BayesNet.getNodeName(i));
                              item.addActionListener(addParentAction);
                              addArcMenu.add(item);
                              nCandidates++;
                        }
                  }
                  if (nCandidates == 0) {
                        addArcMenu.setEnabled(false);
                  }
                  }
                  m_nPosX = nPosX;
                  m_nPosY = nPosY;
                  popupMenu.setLocation(me.getX(), me.getY());
                  popupMenu.show(m_GraphPanel, me.getX(), me.getY());
          } // handleRightClick
            
          /* pop up menu with actions that apply to node that was clicked on
           */
          void handleRightNodeClick(MouseEvent me) {
                  m_Selection.clear();
                  repaint();
                  ActionListener renameValueAction = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              renameValue(m_nCurrentNode, ae.getActionCommand());
                        }
                  };
                  ActionListener delValueAction = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              delValue(m_nCurrentNode, ae.getActionCommand());
                        }
                  };
                  ActionListener addParentAction = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              try {
                                    m_BayesNet.addArc(ae.getActionCommand(), m_BayesNet.getNodeName(m_nCurrentNode));
                                    m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                                    updateStatus();
                              } catch (Exception e) {
                                    e.printStackTrace();
                              }
                        }
                  };
                  ActionListener delParentAction = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              deleteArc(m_nCurrentNode, ae.getActionCommand());
                        }
                  };
                  ActionListener delChildAction = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              deleteArc(ae.getActionCommand(), m_nCurrentNode);
                        }
                  };
                  ActionListener setAvidenceAction = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              try {
                                    String [] outcomes = m_BayesNet.getValues(m_nCurrentNode);
                                    int iValue = 0;
                                    while (iValue < outcomes.length && !outcomes[iValue].equals(ae.getActionCommand())) {
                                          iValue++;
                                    }

                                    if (iValue == outcomes.length) {
                                          iValue = -1;
                                    }
                                    if (iValue < outcomes.length) {
                                        m_jStatusBar.setText("Set evidence for " + m_BayesNet.getNodeName(m_nCurrentNode));
                                                if (m_BayesNet.getEvidence(m_nCurrentNode) < 0 && iValue >= 0) {
                                                      m_BayesNet.setEvidence(m_nCurrentNode, iValue);
                                                      m_marginCalculatorWithEvidence.setEvidence(m_nCurrentNode, iValue);
                                                } else {
                                                      m_BayesNet.setEvidence(m_nCurrentNode, iValue);
                                                      SerializedObject so = new SerializedObject(m_marginCalculator);
                                                      m_marginCalculatorWithEvidence = (MarginCalculator) so.getObject();
                                                      for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                                                            if (m_BayesNet.getEvidence(iNode) >= 0) {
                                                                  m_marginCalculatorWithEvidence.setEvidence(iNode, m_BayesNet.getEvidence(iNode));
                                                            }
                                                      }
                                                }
                                          for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                                                m_BayesNet.setMargin(iNode, m_marginCalculatorWithEvidence.getMargin(iNode));
                                          }
                                    }
                                    } catch (Exception e) {
                                          e.printStackTrace();
                                    }
                                    repaint();
                        }
                  };
                  
                  ActionListener act = new ActionListener() {
                        public void actionPerformed(ActionEvent ae) {
                              if (ae.getActionCommand().equals("Rename")) {
                                    renameNode(m_nCurrentNode);
                                    return;
                              }
                              if (ae.getActionCommand().equals("Add parent")) {
                                    addArcInto(m_nCurrentNode);
                                    return;
                              }
                              if (ae.getActionCommand().equals("Add value")) {
                                    addValue();
                                    return;
                              }
                              if (ae.getActionCommand().equals("Delete node")) {
                                    deleteNode(m_nCurrentNode);
                                    return;
                              }
                              if (ae.getActionCommand().equals("Edit CPT")) {
                                    editCPT(m_nCurrentNode);
                                    return;
                              }
                              repaint();
                        }
                  };
                  try {
                  JPopupMenu popupMenu = new JPopupMenu("Choose a value");

                  JMenu setEvidenceMenu = new JMenu("Set evidence");
                  String [] outcomes = m_BayesNet.getValues(m_nCurrentNode);
                  for (int iValue = 0; iValue < outcomes.length; iValue++) {
                        JMenuItem item = new JMenuItem(outcomes[iValue]);
                        item.addActionListener(setAvidenceAction);
                        setEvidenceMenu.add(item);
                  }
                  setEvidenceMenu.addSeparator();
                  JMenuItem item = new JMenuItem("Clear");
                  item.addActionListener(setAvidenceAction);
                  setEvidenceMenu.add(item);
                  popupMenu.add(setEvidenceMenu);

                  setEvidenceMenu.setEnabled(m_bViewMargins);

                  popupMenu.addSeparator();

                  JMenuItem renameItem = new JMenuItem("Rename");
                  renameItem.addActionListener(act);
                  popupMenu.add(renameItem);

                  JMenuItem delNodeItem = new JMenuItem("Delete node");
                  delNodeItem.addActionListener(act);
                  popupMenu.add(delNodeItem);

                  JMenuItem editCPTItem = new JMenuItem("Edit CPT");
                  editCPTItem.addActionListener(act);
                  popupMenu.add(editCPTItem);

                  popupMenu.addSeparator();
                  
                  JMenu addArcMenu = new JMenu("Add parent");
                  popupMenu.add(addArcMenu);
                  int nNodes = m_BayesNet.getNrOfNodes();
                  boolean[] isNotAllowedAsParent = new boolean[nNodes];
                  // prevent it being a parent of itself
                  isNotAllowedAsParent[m_nCurrentNode] = true;
                  // prevent a descendant being a parent, since it introduces cycles
                  for (int i = 0; i < nNodes; i++) {
                        for (int iNode = 0; iNode < nNodes; iNode++) {
                              for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
                                    if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) {
                                          isNotAllowedAsParent[iNode] = true;
                                    }
                              }
                        }
                  }
                  // prevent nodes that are already a parent
                  for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(m_nCurrentNode); iParent++) {
                        isNotAllowedAsParent[m_BayesNet.getParent(m_nCurrentNode, iParent)] = true;
                  }
                  // count nr of remaining candidates
                  int nCandidates = 0;
                  for (int i = 0; i < nNodes; i++) {
                        if (!isNotAllowedAsParent[i]) {
                              item = new JMenuItem(m_BayesNet.getNodeName(i));
                              item.addActionListener(addParentAction);
                              addArcMenu.add(item);
                              nCandidates++;
                        }
                  }
                  if (nCandidates == 0) {
                        addArcMenu.setEnabled(false);
                  }
                                                      
                  JMenu delArcMenu = new JMenu("Delete parent");
                  popupMenu.add(delArcMenu);
                  if (m_BayesNet.getNrOfParents(m_nCurrentNode) == 0) {
                        delArcMenu.setEnabled(false);
                  }
                  for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(m_nCurrentNode); iParent++) {
                        item = new JMenuItem(m_BayesNet.getNodeName(m_BayesNet.getParent(m_nCurrentNode, iParent)));
                        item.addActionListener(delParentAction);
                        delArcMenu.add(item);
                  }
                  JMenu delChildMenu = new JMenu("Delete child");
                  popupMenu.add(delChildMenu);
                  FastVector nChildren = m_BayesNet.getChildren(m_nCurrentNode); 
                  if (nChildren.size() == 0) {
                        delChildMenu.setEnabled(false);
                  }
                  for (int iChild = 0; iChild < nChildren.size(); iChild++) {
                        item = new JMenuItem(m_BayesNet.getNodeName((Integer) nChildren.elementAt(iChild)));
                        item.addActionListener(delChildAction);
                        delChildMenu.add(item);
                  }

                  popupMenu.addSeparator();

                  JMenuItem addValueItem = new JMenuItem("Add value");
                  addValueItem.addActionListener(act);
                  popupMenu.add(addValueItem);

                  JMenu renameValue = new JMenu("Rename value");
                  popupMenu.add(renameValue);
                  for (int iValue = 0; iValue < outcomes.length; iValue++) {
                        item = new JMenuItem(outcomes[iValue]);
                        item.addActionListener(renameValueAction);
                        renameValue.add(item);
                  }

                  JMenu delValue = new JMenu("Delete value");
                  popupMenu.add(delValue);
                  if (m_BayesNet.getCardinality(m_nCurrentNode) <= 2) {
                        delValue.setEnabled(false);
                  }
                  for (int iValue = 0; iValue < outcomes.length; iValue++) {
                        JMenuItem delValueItem = new JMenuItem(outcomes[iValue]);
                        delValueItem.addActionListener(delValueAction);
                        delValue.add(delValueItem);
                  }
                  
                  popupMenu.setLocation(me.getX(), me.getY());
                  popupMenu.show(m_GraphPanel, me.getX(), me.getY());
                  } catch (Exception e) {
                        e.printStackTrace();
                  }
            } // handleRightNodeClick
      } // class GraphVisualizerMouseListener

      /**
       * private class for handling mouseMoved events to highlight nodes if the
       * the mouse is moved on one, move it around or move selection around
       */
02867       private class GraphVisualizerMouseMotionListener extends MouseMotionAdapter {

            /* last node moved over. Used for turning highlight on and off */
            int m_nLastNode = -1;
            /* current mouse position clicked */
            int m_nPosX, m_nPosY;

            /* identify the node under the mouse
             * @returns node index of node under mouse, or -1 if there is no such node 
             */
            int getGraphNode(MouseEvent me) {
                  m_nPosX = m_nPosY  = 0;

                  Rectangle r = new Rectangle(0, 0, (int) (m_nPaddedNodeWidth * m_fScale), (int) (m_nNodeHeight * m_fScale));
                  m_nPosX += me.getX();
                  m_nPosY += me.getY();

                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {                   
                        r.x = (int) (m_BayesNet.getPositionX(iNode) * m_fScale);
                        r.y = (int) (m_BayesNet.getPositionY(iNode) * m_fScale);
                        if (r.contains(m_nPosX, m_nPosY)) {
                              return iNode;
                        }
                  }
                  return -1;
            } // getGraphNode

            /* handle mouse dragging event
             *  (non-Javadoc)
             * @see java.awt.event.MouseMotionListener#mouseDragged(java.awt.event.MouseEvent)
             */
            public void mouseDragged(MouseEvent me) {
                  if (m_nSelectedRect != null) {
                        m_nSelectedRect.width = me.getPoint().x - m_nSelectedRect.x;
                        m_nSelectedRect.height = me.getPoint().y - m_nSelectedRect.y;
                        repaint();
                        return;
                  }
                  int iNode = getGraphNode(me);
                  if (iNode >= 0) {
                        if (m_Selection.getSelected().size() > 0) {
                              if (m_Selection.getSelected().contains(iNode)) {
                                    m_BayesNet.setPosition(iNode, (int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)),
                                          (int) ((m_nPosY / m_fScale - m_nNodeHeight / 2)), m_Selection.getSelected());
                              } else {
                                    m_Selection.clear();
                                    m_BayesNet.setPosition(iNode, (int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)),
                                                (int) ((m_nPosY / m_fScale - m_nNodeHeight / 2)));
                              }
                              repaint();
                        } else {
                              m_BayesNet.setPosition(iNode, (int) ((m_nPosX / m_fScale - m_nPaddedNodeWidth / 2)),
                                    (int) ((m_nPosY / m_fScale - m_nNodeHeight / 2)));
                        }
                      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                        a_undo.setEnabled(true);
                        a_redo.setEnabled(false);
                        m_GraphPanel.highLight(iNode);
                  }
                  if (iNode < 0) {
                        if (m_nLastNode >= 0) {
                              m_GraphPanel.repaint();
                              m_nLastNode = -1;
                        } else {
                              m_nSelectedRect = new Rectangle(me.getPoint().x, me.getPoint().y, 1, 1);
                              m_GraphPanel.repaint();
                        }
                  }
            } // mouseDragged
            
            /* handles mouse move event
             *  (non-Javadoc)
             * @see java.awt.event.MouseMotionListener#mouseMoved(java.awt.event.MouseEvent)
             */
            public void mouseMoved(MouseEvent me) {
                  int iNode = getGraphNode(me);
                  if (iNode >= 0) {
                        if (iNode != m_nLastNode) {
                              m_GraphPanel.highLight(iNode);
                              if (m_nLastNode >= 0) {
                                    m_GraphPanel.highLight(m_nLastNode);
                              }
                              m_nLastNode = iNode;
                        }
                  }
                  if (iNode < 0 && m_nLastNode >= 0) {
                        m_GraphPanel.repaint();
                        m_nLastNode = -1;
                  }
            } // mouseMoved
            
      } // class GraphVisualizerMouseMotionListener

      /* apply graph layout algorithm to Bayesian network 
       */
      void layoutGraph() {
            if (m_BayesNet.getNrOfNodes() == 0) {
                  return;
            }
            try {
                  FastVector m_nodes = new FastVector();
                  FastVector m_edges = new FastVector();
                  BIFParser bp = new BIFParser(m_BayesNet.toXMLBIF03(), m_nodes, m_edges);
                  bp.parse();
                  updateStatus();
                  m_layoutEngine = new HierarchicalBCEngine(m_nodes, m_edges, m_nPaddedNodeWidth, m_nNodeHeight);
                  m_layoutEngine.addLayoutCompleteEventListener(this);
                  m_layoutEngine.layoutGraph();
            } catch (Exception e) {
                  e.printStackTrace();
            }
      } // layoutGraph
      
      /* Update status of various items that need regular updating
       * such as enabled status of some menu items, marginal distributions
       * if shown, repainting of graph.
       */
      void updateStatus() {
            a_undo.setEnabled(m_BayesNet.canUndo());
            a_redo.setEnabled(m_BayesNet.canRedo());

            a_datagenerator.setEnabled(m_BayesNet.getNrOfNodes() > 0);

            if (!m_bViewMargins && !m_bViewCliques) {
                  repaint();
                  return;
            }

            try {
                  m_marginCalculator = new MarginCalculator();
                  m_marginCalculator.calcMargins(m_BayesNet);
                  SerializedObject so = new SerializedObject(m_marginCalculator);
                  m_marginCalculatorWithEvidence = (MarginCalculator) so.getObject();
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        if (m_BayesNet.getEvidence(iNode) >= 0) {
                              m_marginCalculatorWithEvidence.setEvidence(iNode, m_BayesNet.getEvidence(iNode));
                        }
                  }
                  for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                        m_BayesNet.setMargin(iNode, m_marginCalculatorWithEvidence.getMargin(iNode));
                  }
            } catch (Exception e) {
                  e.printStackTrace();
            }
            repaint();
      } // updateStatus

      /* add arc with node iChild as child. 
       * This pops up a selection list with potential parents for the child.
       * All decendants and current parents are excluded from the list as is
       * the child node itself.
       * @param iChild index of the node for which to add an arc
       */
      void addArcInto(int iChild) {
            String sChild = m_BayesNet.getNodeName(iChild);
            try {
                  int nNodes = m_BayesNet.getNrOfNodes();
                  boolean[] isNotAllowedAsParent = new boolean[nNodes];
                  // prevent it being a parent of itself
                  isNotAllowedAsParent[iChild] = true;
                  // prevent a descendant being a parent, since it introduces cycles
                  for (int i = 0; i < nNodes; i++) {
                        for (int iNode = 0; iNode < nNodes; iNode++) {
                              for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iNode); iParent++) {
                                    if (isNotAllowedAsParent[m_BayesNet.getParent(iNode, iParent)]) {
                                          isNotAllowedAsParent[iNode] = true;
                                    }
                              }
                        }
                  }
                  // prevent nodes that are already a parent
                  for (int iParent = 0; iParent < m_BayesNet.getNrOfParents(iChild); iParent++) {
                        isNotAllowedAsParent[m_BayesNet.getParent(iChild, iParent)] = true;
                  }
                  // count nr of remaining candidates
                  int nCandidates = 0;
                  for (int i = 0; i < nNodes; i++) {
                        if (!isNotAllowedAsParent[i]) {
                              nCandidates++;
                        }
                  }
                  if (nCandidates == 0) {
                        JOptionPane.showMessageDialog(null, "No potential parents available for this node (" + sChild
                                    + "). Choose another node as child node.");
                        return;
                  }
                  String[] options = new String[nCandidates];
                  int k = 0;
                  for (int i = 0; i < nNodes; i++) {
                        if (!isNotAllowedAsParent[i]) {
                              options[k++] = m_BayesNet.getNodeName(i);
                        }
                  }
                  String sParent = (String) JOptionPane.showInputDialog(null, "Select parent node for " + sChild, "Nodes", 0,
                              null, options, options[0]);
                  if (sParent == null || sParent.equals("")) {
                        return;
                  }
                  // update all data structures
                  m_BayesNet.addArc(sParent, sChild);
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                updateStatus();
            } catch (Exception e) {
                  e.printStackTrace();
            }
      } // addArcInto

      /* deletes arc from node with name sParent into child with index iChild
       * 
       */
      void deleteArc(int iChild, String sParent) {
            try {
                  m_BayesNet.deleteArc(m_BayesNet.getNode(sParent), iChild);
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
            } catch (Exception e) {
                  e.printStackTrace();
            }
            updateStatus();
      } // deleteArc
      
      /* deletes arc from node with index iParent into child with name sChild
       * 
       */
      void deleteArc(String sChild, int iParent) {
            try {
                  m_BayesNet.deleteArc(iParent, m_BayesNet.getNode(sChild));
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
            } catch (Exception e) {
                  e.printStackTrace();
            }
            updateStatus();
      } // deleteArc

      /* deletes arc. Pops up list of arcs listed in 'options' as 
       * "<Node1> -> <Node2>".
       */
      void deleteArc(String[] options) {
            String sResult = (String) JOptionPane.showInputDialog(null, "Select arc to delete", "Arcs", 0, null, options,
                        options[0]);
            if (sResult != null && !sResult.equals("")) {
                  int nPos = sResult.indexOf(" -> ");
                  String sParent = sResult.substring(0, nPos);
                  String sChild = sResult.substring(nPos + 4);
                  try {
                        m_BayesNet.deleteArc(sParent, sChild);
                      m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  } catch (Exception e) {
                        e.printStackTrace();
                  }
                  updateStatus();
            }
      } // deleteArc

      /* Rename node with index nTargetNode.
       * Pops up window that allwos for entering a new name.
       */
      void renameNode(int nTargetNode) {
            String sName = (String) JOptionPane.showInputDialog(null, m_BayesNet.getNodeName(nTargetNode), "New name for node",
                        JOptionPane.OK_CANCEL_OPTION);
            if (sName == null || sName.equals("")) {
                  return;
            }
            try {
                  while (m_BayesNet.getNode2(sName) >= 0) {
                        sName = (String) JOptionPane.showInputDialog(null, "Cannot rename to " + sName
                                    + ".\nNode with that name already exists.");
                        if (sName == null || sName.equals("")) {
                              return;
                        }
                  }
                  m_BayesNet.setNodeName(nTargetNode, sName);
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
            } catch (Exception e) {
                  e.printStackTrace();
            }
            repaint();
      } // renameNode

      /* Rename value with name sValeu of a node with index nTargetNode.
       * Pops up window that allows entering a new name.
       */
      void renameValue(int nTargetNode, String sValue) {
            String sNewValue = (String) JOptionPane.showInputDialog(null, "New name for value " + sValue, "Node "
                        + m_BayesNet.getNodeName(nTargetNode), JOptionPane.OK_CANCEL_OPTION);
            if (sNewValue == null || sNewValue.equals("")) {
                  return;
            }
            m_BayesNet.renameNodeValue(nTargetNode, sValue, sNewValue);
          m_jStatusBar.setText(m_BayesNet.lastActionMsg());
            a_undo.setEnabled(true);
            a_redo.setEnabled(false);
            repaint();
      } // renameValue

      /* delete a single node with index iNode */
      void deleteNode(int iNode) {
            try {
                  m_BayesNet.deleteNode(iNode);
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
            } catch (Exception e) {
                  e.printStackTrace();
            }
            updateStatus();
      } // deleteNode

      /* Add a value to currently selected node.
       * Shows window that allows to enter the name of the value. 
       */
      void addValue() {
            //GraphNode n = (GraphNode) m_nodes.elementAt(m_nCurrentNode);
            String sValue = "Value" + (m_BayesNet.getCardinality(m_nCurrentNode) + 1);
            String sNewValue = (String) JOptionPane.showInputDialog(null, "New value " + sValue, "Node " + m_BayesNet.getNodeName(m_nCurrentNode),
                        JOptionPane.OK_CANCEL_OPTION);
            if (sNewValue == null || sNewValue.equals("")) {
                  return;
            }
            try {
                  m_BayesNet.addNodeValue(m_nCurrentNode, sNewValue);
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                  //n.outcomes = m_BayesNet.getValues(m_nCurrentNode);
                  //for (int iNode = 0; iNode < m_BayesNet.getNrOfNodes(); iNode++) {
                  //    n = (GraphNode) m_nodes.elementAt(iNode);
                  //    n.probs = m_BayesNet.getDistribution(iNode);
                  //}
            } catch (Exception e) {
                  e.printStackTrace();
            }
            updateStatus();
      } // addValue

      /* remove value with name sValue from the node with index nTargetNode
       */
      void delValue(int nTargetNode, String sValue) {
            try {
                  m_BayesNet.delNodeValue(nTargetNode, sValue);
                m_jStatusBar.setText(m_BayesNet.lastActionMsg());
            } catch (Exception e) {
                  e.printStackTrace();
            }
            updateStatus();
      } // delValue

      /* Edits CPT of node with index nTargetNode. 
       * Pops up table with probability table that the user can change or just view.
       */
      void editCPT(int nTargetNode) {
            m_nCurrentNode = nTargetNode;
            final GraphVisualizerTableModel tm = new GraphVisualizerTableModel(nTargetNode);

            JTable jTblProbs = new JTable(tm); 

            JScrollPane js = new JScrollPane(jTblProbs);

            int nParents = m_BayesNet.getNrOfParents(nTargetNode);
            if (nParents > 0) {
                  GridBagConstraints gbc = new GridBagConstraints();
                  JPanel jPlRowHeader = new JPanel(new GridBagLayout());

                  // indices of the parent nodes in the Vector
                  int[] idx = new int[nParents];
                  // max length of values of each parent
                  int[] lengths = new int[nParents];

                  // Adding labels for rows
                  gbc.anchor = GridBagConstraints.NORTHWEST;
                  gbc.fill = GridBagConstraints.HORIZONTAL;
                  gbc.insets = new Insets(0, 1, 0, 0);
                  int addNum = 0, temp = 0;
                  boolean dark = false;
                  while (true) {
                        gbc.gridwidth = 1;
                        for (int k = 0; k < nParents; k++) {
                              int iParent2 = m_BayesNet.getParent(nTargetNode, k); 
                              JLabel lb = new JLabel(m_BayesNet.getValueName(iParent2,idx[k]));
                              lb.setFont(new Font("Dialog", Font.PLAIN, 12));
                              lb.setOpaque(true);
                              lb.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 1));
                              lb.setHorizontalAlignment(JLabel.CENTER);
                              if (dark) {
                                    lb.setBackground(lb.getBackground().darker());
                                    lb.setForeground(Color.white);
                              } else
                                    lb.setForeground(Color.black);

                              temp = lb.getPreferredSize().width;
                              lb.setPreferredSize(new Dimension(temp, jTblProbs.getRowHeight()));
                              if (lengths[k] < temp)
                                    lengths[k] = temp;
                              temp = 0;

                              if (k == nParents - 1) {
                                    gbc.gridwidth = GridBagConstraints.REMAINDER;
                                    dark = (dark == true) ? false : true;
                              }
                              jPlRowHeader.add(lb, gbc);
                              addNum++;
                        }

                        for (int k = nParents - 1; k >= 0; k--) {
                              int iParent2 = m_BayesNet.getParent(m_nCurrentNode, k);
                              if (idx[k] == m_BayesNet.getCardinality(iParent2) - 1 && k != 0) {
                                    idx[k] = 0;
                                    continue;
                              } else {
                                    idx[k]++;
                                    break;
                              }
                        }

                        int iParent2 = m_BayesNet.getParent(m_nCurrentNode, 0);
                        if (idx[0] == m_BayesNet.getCardinality(iParent2)) {
                              JLabel lb = (JLabel) jPlRowHeader.getComponent(addNum - 1);
                              jPlRowHeader.remove(addNum - 1);
                              lb.setPreferredSize(new Dimension(lb.getPreferredSize().width, jTblProbs
                                          .getRowHeight()));
                              gbc.gridwidth = GridBagConstraints.REMAINDER;
                              gbc.weighty = 1;
                              jPlRowHeader.add(lb, gbc);
                              gbc.weighty = 0;
                              break;
                        }
                  }

                  gbc.gridwidth = 1;
                  // The following panel contains the names of the
                  // parents
                  // and is displayed above the row names to identify
                  // which value belongs to which parent
                  JPanel jPlRowNames = new JPanel(new GridBagLayout());
                  for (int j = 0; j < nParents; j++) {
                        JLabel lb2;
                        JLabel lb1 = new JLabel(m_BayesNet.getNodeName(m_BayesNet.getParent(nTargetNode, j)));
                        lb1.setBorder(BorderFactory.createEmptyBorder(1, 2, 1, 1));
                        Dimension tempd = lb1.getPreferredSize();
                        if (tempd.width < lengths[j]) {
                              lb1.setPreferredSize(new Dimension(lengths[j], tempd.height));
                              lb1.setHorizontalAlignment(JLabel.CENTER);
                              lb1.setMinimumSize(new Dimension(lengths[j], tempd.height));
                        } else if (tempd.width > lengths[j]) {
                              lb2 = (JLabel) jPlRowHeader.getComponent(j);
                              lb2.setPreferredSize(new Dimension(tempd.width, lb2.getPreferredSize().height));
                        }
                        jPlRowNames.add(lb1, gbc);
                  }
                  js.setRowHeaderView(jPlRowHeader);
                  js.setCorner(JScrollPane.UPPER_LEFT_CORNER, jPlRowNames);
            }

            final JDialog dlg = new JDialog((Frame) GUI.this.getTopLevelAncestor(),
                        "Probability Distribution Table For " + m_BayesNet.getNodeName(nTargetNode), true);
            dlg.setSize(500, 400);
            dlg.setLocation(GUI.this.getLocation().x + GUI.this.getWidth() / 2
                        - 250, GUI.this.getLocation().y + GUI.this.getHeight() / 2
                        - 200);

            dlg.getContentPane().setLayout(new BorderLayout());
            dlg.getContentPane().add(js, BorderLayout.CENTER);

            JButton jBtRandomize = new JButton("Randomize");
            jBtRandomize.setMnemonic('R');
            jBtRandomize.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent ae) {
                        tm.randomize();
                        dlg.repaint();
                  }
            });
            
            JButton jBtOk = new JButton("Ok");
            jBtOk.setMnemonic('O');
            jBtOk.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent ae) {
                        tm.setData();
                        try {
                              m_BayesNet.setDistribution(m_nCurrentNode, tm.m_fProbs);
                            m_jStatusBar.setText(m_BayesNet.lastActionMsg());
                              updateStatus();
                        } catch (Exception e) {
                              e.printStackTrace();
                        }
                        dlg.setVisible(false);
                  }
            });
            JButton jBtCancel = new JButton("Cancel");
            jBtCancel.setMnemonic('C');
            jBtCancel.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent ae) {
                        dlg.setVisible(false);
                  }
            });
            Container c = new Container();
            c.setLayout(new GridBagLayout());
            c.add(jBtRandomize);
            c.add(jBtOk);
            c.add(jBtCancel);

            dlg.getContentPane().add(c, BorderLayout.SOUTH);
            dlg.setVisible(true);
      } // editCPT

      /**
       * Main method. Builds up menus and reads from file if one is specified.
       */
03369       public static void main(String[] args) {
            JFrame jf = new JFrame("Bayes Network Editor");
            final GUI g = new GUI();

            if (args.length>0) {
                  try {
                        g.readBIFFromFile(args[0]);
                  } catch (IOException ex) {
                        ex.printStackTrace();
                  } catch (BIFFormatException bf) {
                        bf.printStackTrace();
                        System.exit(-1);
                  }
            }

            JMenuBar menuBar = new JMenuBar();
            JMenu fileMenu = new JMenu("File");
            fileMenu.setMnemonic('F');

            menuBar.add(fileMenu);
            fileMenu.add(g.a_new);
            fileMenu.add(g.a_load);
            fileMenu.add(g.a_save);
            fileMenu.add(g.a_saveas);
            fileMenu.addSeparator();
            fileMenu.add(g.a_print);
            fileMenu.add(g.a_export);
            fileMenu.addSeparator();
            fileMenu.add(g.a_quit);
            JMenu editMenu = new JMenu("Edit");
            editMenu.setMnemonic('E');
            menuBar.add(editMenu);
            editMenu.add(g.a_undo);
            editMenu.add(g.a_redo);
            editMenu.addSeparator();
            editMenu.add(g.a_selectall);
            editMenu.add(g.a_delnode);
            editMenu.add(g.a_cutnode);
            editMenu.add(g.a_copynode);
            editMenu.add(g.a_pastenode);
            editMenu.addSeparator();
            editMenu.add(g.a_addnode);
            editMenu.add(g.a_addarc);
            editMenu.add(g.a_delarc);
            editMenu.addSeparator();
            editMenu.add(g.a_alignleft);
            editMenu.add(g.a_alignright);
            editMenu.add(g.a_aligntop);
            editMenu.add(g.a_alignbottom);
            editMenu.add(g.a_centerhorizontal);
            editMenu.add(g.a_centervertical);
            editMenu.add(g.a_spacehorizontal);
            editMenu.add(g.a_spacevertical);

            JMenu toolMenu = new JMenu("Tools");
            toolMenu.setMnemonic('T');
            toolMenu.add(g.a_networkgenerator);
            toolMenu.add(g.a_datagenerator);
            toolMenu.add(g.a_datasetter);
            toolMenu.add(g.a_learn);
            toolMenu.add(g.a_learnCPT);
            toolMenu.addSeparator();
            toolMenu.add(g.a_layout);
            toolMenu.addSeparator();
            final JCheckBoxMenuItem viewMargins = new JCheckBoxMenuItem("Show Margins", false);
            viewMargins.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent ae) {
                        boolean bPrev = g.m_bViewMargins; 
                        g.m_bViewMargins = viewMargins.getState();
                        if (bPrev == false && viewMargins.getState() == true) {
                              g.updateStatus();
                        }
                        g.repaint();
                  }
            });
            toolMenu.add(viewMargins);
            final JCheckBoxMenuItem viewCliques = new JCheckBoxMenuItem("Show Cliques", false);
            viewCliques.addActionListener(new ActionListener() {
                  public void actionPerformed(ActionEvent ae) {
                        boolean bPrev = g.m_bViewCliques; 
                        g.m_bViewCliques = viewCliques.getState();
                        if (bPrev == false && viewCliques.getState() == true) {
                              g.updateStatus();
                        }
                        g.repaint();
                  }
            });
            toolMenu.add(viewCliques);

            menuBar.add(toolMenu);
            JMenu viewMenu = new JMenu("View");
            viewMenu.setMnemonic('V');
            menuBar.add(viewMenu);
            viewMenu.add(g.a_zoomin);
            viewMenu.add(g.a_zoomout);
            viewMenu.addSeparator();
            viewMenu.add(g.a_viewtoolbar);
            viewMenu.add(g.a_viewstatusbar);
            jf.setJMenuBar(menuBar);
            JMenu helpMenu = new JMenu("Help");
            helpMenu.setMnemonic('H');
            menuBar.add(helpMenu);
            helpMenu.add(g.a_help);
            helpMenu.add(g.a_about);


            
            jf.getContentPane().add(g);
            jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            jf.setSize(800, 600);
            jf.setVisible(true);
            g.m_Selection.updateGUI();
            GenericObjectEditor.registerEditors();
      } // main
      
} // end of class

Generated by  Doxygen 1.6.0   Back to index