Package weka.classifiers.bayes.net

Source Code of weka.classifiers.bayes.net.GUI$GraphVisualizerMouseMotionListener

/*
*    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 $
*/
public class GUI extends JPanel implements LayoutCompleteEventListener {

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

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

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

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

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

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

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

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

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

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

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

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


  /** used when using zoomIn and zoomOut buttons */
  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 */
  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 */
  int m_nCurrentNode = -1;
  /** selection of nodes */
  Selection m_Selection = new Selection();
  /** selection rectangle drawn through dragging with left mouse button */
  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.
   * */
  class MyAction extends AbstractAction {
    /** for serialization */
    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.
   */
  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.
   */
  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.
   */
  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.
   */
  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
   */
  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.
   */
  private class GraphPanel extends PrintablePanel implements Printable {

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

    /** node drawing modes */
    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 */
    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.
     */
    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
     */
    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
       */
    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
   */
  private class GraphVisualizerTableModel extends AbstractTableModel {

    /** for serialization */
    private static final long serialVersionUID = -4789813491347366596L;
    /** labels for the columns */
    final String [] m_sColumnNames;
    /** probability table data **/
    final double [][] m_fProbs;
    /** nr of node for currently editted CPT */
    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
     */
    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 */
    public int getColumnCount() {
      return m_sColumnNames.length;
    }

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

    /** return name of specified colum
     * @param iCol index of the column
     */
    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
     */
    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
     */
    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
   */
  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.
     */
    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 */
      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
   */
  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.
   */
  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
TOP

Related Classes of weka.classifiers.bayes.net.GUI$GraphVisualizerMouseMotionListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.