package causation.lab;
// Sun packages
import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import java.util.*;
import java.net.URL;
// our packages
import tetrad.graph.*;
import tetrad.model.*;
// custom classes
import causation.lab.WorkbenchModel;
import causation.lab.plaf.WorkbenchUI;
import causation.lab.WBVariable;
import causation.lab.Randomizer;
import causation.lab.Latent;
import causation.lab.WorkbenchEdge;
import causation.lab.ScreenShot;
import causation.lab.WorkbenchObject;
import stats.DataTableModel;
import stats.TextVariable;
/**
*
The Workbench provides a "workspace" for manipulating WBVariables, which
* are based on an underlying BayesNet. The Workbench maintains the
* WBVariables, and provides a JComponent on which to move and draw them. It
* also allows the user to manipulate them in various ways, depending on
* the mode of the Workbench. Note: It's important to keep the underlying
* "true" BayesNet distinct in one's coding from the DiGraph being
* constructed by the user by drawing arrows.
*
* The following PropertyChangeEvent
s are sent by this class:
*
* - ("bayesNet", null, null)
*
- ("tetradGraph", null, null)
*
- ("allvariables", null, null)
*
- ("variable"+index#, String oldName, String newName)
*
- Values different, unless the new WBVariable has the same name
* - ("mode", Integer oldMode, Integer newMode)
*
*
- ("tool", Integer oldTool, Integer newTool)
*
*
- ("lock:"+varname, String oldValue, String newValue)
*
*
- ("randomized:"+varname, Boolean oldState, Boolean newState)
*
*
- ("onWorkbench:"+varname, Boolean oldState, Boolean newState)
*
*
- ("edgeadded", String fromName, String toName)
*
*
- ("edgeremoved", String fromName, String toName)
*
*
- ("Performed operation", null, null)
*
*
* Copyright 1999 by David Danks. All rights reserved.
*
*
* @see WBVariable
* @see Randomizer
* @see Latent
* @version 0.9 Aug 18, 1999
* @author David Danks
*/
public class Workbench extends JComponent implements LayoutManager,
PropertyChangeListener, MouseListener, MouseMotionListener,
ActionListener {
/////////////////// Static Class Variables ///////////////////
// mode and tool constants (since we want the model to be invisible
// to classes using the Workbench.
public static final int SETUP_GATHER_MODE =
WorkbenchModel.SETUP_GATHER_MODE;
public static final int HYPOTHESIZE_PREDICT_MODE =
WorkbenchModel.HYPOTHESIZE_PREDICT_MODE;
public static final int MOVE = WorkbenchModel.MOVE;
public static final int LOCK = WorkbenchModel.LOCK;
public static final int RANDOMIZER = WorkbenchModel.RANDOMIZER;
public static final int ARROW = WorkbenchModel.ARROW;
public static final int LATENT = WorkbenchModel.LATENT;
public static final int GATHER_DATA = -10000; // these two are used only
public static final int INDEPENDENCE = -10001; // to describe functions
// GUI settings
private static final int DEFAULT_WIDTH = 400;
private static final int DEFAULT_HEIGHT = 400;
private static final int MINIMUM_WIDTH = 50;
private static final int VARIABLE_SEPARATION = 3;
private static final int BIN_DIVIDER_WIDTH = 1;
private static final Insets WB_MARGIN = new Insets(3, 3, 3, 3);
private static final Insets BIN_MARGIN = new Insets(3, 3, 3, 3);
private static final Font FONT = new Font("Dialog", Font.BOLD, 14);
private static final String BIN_LABEL = "Variable bin";
private static final String WORKBENCH_LABEL = "Workbench";
/////////////////// Instance Variables ///////////////////
private static final String UIClassID = "causation.lab.plaf.WorkbenchUI";
private WorkbenchModel model;
private WBVariable[] variables;
private Vector onWorkbench;
private Vector randomizers;
private Vector latents;
private Vector screenShots = new Vector();
private int numCreatedLatents = 0;
// GUI variables
private int BIN_HEIGHT;
private Rectangle bin_rect = new Rectangle();
private int var_width;
private Point clickPoint = new Point();
private boolean drawingEdge = false;
private boolean movingVariable = false;
private Vector edges;
private Object selected;
private URL codebase;
/////////////////// Constructors ///////////////////
/**
* Creates a workbench based on a trivial BayesNetIM, with all images
* loaded from the given codebase, and in the Setup & Gather Data mode.
*
* @param codebase The codebase to load the images from
*/
public Workbench(URL codebase) {
init(new WorkbenchModel(), codebase);
}
/**
* Creates a workbench based on the given BayesNetIM, with all images
* loaded from the given codebase, and in the Setup & Gather Data mode.
*
* @param bn The underlying BayesNetIM for the Workbench
* @param codebase The codebase to load the images from
*/
public Workbench(BayesNetIM bn, URL codebase) {
init(new WorkbenchModel(bn), codebase);
}
/**
* Creates a workbench based on a trivial BayesNetIM, with all images
* loaded from the given codebase, and in the given mode.
*
* @param mode The mode in which to start the Workbench
* @param codebase The codebase to load the images from
*/
public Workbench(int mode, URL codebase) {
init(new WorkbenchModel(mode), codebase);
}
/**
* Creates a workbench based on the given BayesNetIM, with all images
* loaded from the given codebase, and in the given mode.
*
* @param bn The underlying BayesNetIM for the Workbench
* @param mode The mode in which to start the Workbench
* @param codebase The codebase to load the images from
*/
public Workbench(BayesNetIM bn, int mode, URL codebase) {
init(new WorkbenchModel(bn, mode), codebase);
}
/**
* Helper method for the constructors that does most of the work.
* @param wModel The WorkbenchModel underlying the Workbench
* @param codebase The codebase to load all images from
*/
private void init(WorkbenchModel wModel, URL codebase) {
// initialize the workbench
model = wModel;
setLayout(this);
setFont(FONT);
this.codebase = codebase;
// listen for clicks to create latents, rands, and select edges
addMouseListener(this);
variables = createVariables();
for (int i=0; i=0; i--) {
if ((WorkbenchEdge)edges.elementAt(i) == selected) {
edges.removeElement(selected);
break;
}
}
// remove from TetradGraph
removeEdgeFromTetradGraph((WorkbenchEdge)selected);
selected = null;
repaint();
return;
}
else if (selected instanceof Randomizer) {
// remove from the randomizer Vector
for (int i=randomizers.size()-1; i>=0; i--) {
if ((Randomizer)randomizers.elementAt(i) == selected) {
randomizers.removeElement(selected);
break;
}
}
// remove from the associated variable
WBVariable variable = ((Randomizer)selected).getTarget();
variable.setRandomizer(null);
firePropertyChange("randomized:"+variable.getName(), true, false);
// remove from the layout
remove((Component)selected);
selected = null;
repaint();
return;
}
}
/**
* Called when the user clicks somewhere on the Workbench, or one of its
* Components (WBVariables, Randomizers, or Latents).
* @param m The MouseEvent to respond to
*/
public void mouseClicked(MouseEvent m) {
// ignore, if the frame isn't selected
if (!acceptEvent()) return;
// if they clicked on the workbench itself, then select edges or
// create a new component
if (m.getSource() instanceof Workbench) {
// de-select selected, if there is one
if (selected != null) {
if (selected instanceof WorkbenchEdge)
((WorkbenchEdge)selected).setSelected(false);
else if (selected instanceof Randomizer)
((Randomizer)selected).setSelected(false);
selected = null;
repaint();
}
// if it's a click in the bin, just ignore it
if (bin_rect.contains(m.getPoint())) return;
switch(model.getTool()) {
case MOVE: // only pay attention if the edges could be selected
if (model.getMode() == SETUP_GATHER_MODE) return;
case ARROW: // check for selecting an edge
selectEdge(m.getPoint()); break;
case LATENT: // add a latent
addLatent(m.getPoint()); break;
default: break;
}
firePropertyChange("Performed operation", null, null);
return;
}
// if they clicked on a Rand in SG mode, then select it
if (m.getSource() instanceof Randomizer) {
switch(model.getTool()) {
case MOVE:
if (model.getMode() == HYPOTHESIZE_PREDICT_MODE) return;
case RANDOMIZER:
Randomizer src = (Randomizer)m.getSource();
if (src.isSelected()) return;
src.setSelected(true);
selected = src;
repaint();
break;
default: break;
}
firePropertyChange("Performed operation", null, null);
return;
}
// don't do anything right now
if (m.getSource() instanceof Latent) {
return;
}
// if on a Var, then lock or randomize
if (m.getSource() instanceof WBVariable) {
WBVariable src = (WBVariable)m.getSource();
switch(model.getTool()) {
case LOCK: // if the variable is on the workbench, set the lock
if (onWorkbench.contains(src)) getAndSetLock(src);
break;
case RANDOMIZER: // if it already has a randomizer, ignore
if (src.isRandomized()) return;
addRandomizer(src);
break;
default: break; // the rest of the tools don't respond
}
firePropertyChange("Performed operation", null, null);
return;
}
}
/**
* Called when the user is dragging the mouse (so is either drawing an
* arrow, or moving a Component).
* @param m The MouseEvent to respond to
*/
public void mouseDragged(MouseEvent m) {
// disregard if on Workbench
if (m.getSource() instanceof Workbench) return;
Component comp = (Component)m.getSource();
Point origin = comp.getLocation();
Point current = m.getPoint();
switch(model.getTool()) {
case MOVE:
if (drawingEdge) { // could be doing this if they shift-dragged
((WorkbenchEdge)selected).setEndPoint(origin.x+current.x,
origin.y+current.y);
repaint();
return;
}
comp.setLocation(origin.x+(current.x-clickPoint.x),
origin.y+(current.y-clickPoint.y));
// we need an additional repaint() for moving edges
repaint();
return;
case ARROW:
if (!(drawingEdge)) return; // ignore if not drawing edge
((WorkbenchEdge)selected).setEndPoint(origin.x+current.x,
origin.y+current.y);
repaint();
return;
default: return;
}
}
/**
* Called when the user releases the mouse.
* @param m The MouseEvent to respond to
*/
public void mouseReleased(MouseEvent m) {
// ignore mouseReleasedEvents from the Workbench
if (m.getSource() instanceof Workbench) return;
// if it's a Rand, make sure it's on the bench
else if (m.getSource() instanceof Randomizer) {
Randomizer rand = (Randomizer)m.getSource();
switch(model.getTool()) {
case MOVE:
rand.setLocation(moveOnScreen(rand));
Rectangle oldBounds = rand.getBounds();
oldBounds.y = (oldBounds.y+oldBounds.height >
getInsets().top+getBounds().height-BIN_HEIGHT) ?
getInsets().top+getBounds().height-
BIN_HEIGHT-oldBounds.height : oldBounds.y;
rand.setBounds(oldBounds);
repaint();
break;
default: break;
}
return;
}
// check if it's a Latent
else if (m.getSource() instanceof Latent) {
Latent latent = (Latent)m.getSource();
switch(model.getTool()) {
case ARROW: // finish the WorkbenchEdge
if (!(drawingEdge)) return; // if we weren't drawing, ignore
drawingEdge = false;
boolean onVariable = finishEdge(new
Point(m.getPoint().x+latent.getLocation().x,
m.getPoint().y+latent.getLocation().y));
if (!(onVariable)) { // released off of a variable
edges.removeElement(selected);
selected = null;
repaint();
}
firePropertyChange("Performed operation", null, null);
break;
case MOVE: // if it's off the bench, then remove it
if (drawingEdge) { // could be doing this if they shift-dragged
drawingEdge = false;
onVariable = finishEdge(new
Point(m.getPoint().x+latent.getLocation().x,
m.getPoint().y+latent.getLocation().y));
if (!(onVariable)) { // released off of a variable
edges.removeElement(selected);
selected = null;
repaint();
}
firePropertyChange("Performed operation", null, null);
return;
}
// if it gets here, then it must be an actual move
movingVariable = false;
Rectangle bounds = latent.getBounds();
Insets insets = getInsets();
Dimension size = getSize();
if ((bounds.x < insets.left+WB_MARGIN.left) ||
(bounds.y < insets.top+WB_MARGIN.top+
getFontMetrics(getFont()).getHeight()) ||
(bounds.x+bounds.width > size.width-insets.right-
WB_MARGIN.right) ||
(bounds.y+bounds.height > insets.top+size.height-BIN_HEIGHT))
removeLatent(latent);
break;
default: break;
}
return;
}
// if it's a Var, put it in the right place by shifting, then checking
else if (m.getSource() instanceof WBVariable) {
WBVariable src = (WBVariable)m.getSource();
switch(model.getTool()) {
case MOVE:
if (drawingEdge) { // could be doing this if they shift-dragged
drawingEdge = false;
boolean onVariable = finishEdge(new
Point(m.getPoint().x+src.getLocation().x,
m.getPoint().y+src.getLocation().y));
if (!(onVariable)) { // released off of a variable
edges.removeElement(selected);
selected = null;
repaint();
}
firePropertyChange("Performed operation", null, null);
return;
}
movingVariable = false;
// first, we need to shift the bounds
src.setLocation(moveOnScreen(src));
// then check where it is
if (src.getBounds().intersects(bin_rect))
setOnWorkbench(src, false);
else setOnWorkbench(src, true);
repaint();
break;
case ARROW:
if (!(drawingEdge)) return; // if we weren't drawing, ignore
drawingEdge = false;
boolean onVariable = finishEdge(new
Point(m.getPoint().x+src.getLocation().x,
m.getPoint().y+src.getLocation().y));
if (!(onVariable)) { // released off of a variable
edges.removeElement(selected);
selected = null;
repaint();
}
firePropertyChange("Performed operation", null, null);
break;
default: break;
}
return;
}
}
/**
* Called when the user presses down the mouse
* @param m The MouseEvent to respond to
*/
public void mousePressed(MouseEvent m) {
// ignore mouseEvents from the Workbench
if (m.getSource() instanceof Workbench) return;
// otherwise, our response depends on the current tool
switch(model.getTool()) {
case MOVE:
clickPoint = m.getPoint();
// if SHIFT is down in HP mode with a press on a variable or a latent,
// then draw an arrow instead of moving it.
if ((m.isShiftDown()) && (getMode() == HYPOTHESIZE_PREDICT_MODE)) {
if (selected != null) {
((WorkbenchEdge)selected).setSelected(false);
selected = null;
repaint();
}
if (onWorkbench.contains(m.getSource())) {
drawingEdge = true;
selected = new
WorkbenchEdge((WBVariable)m.getSource(), m.getPoint());
edges.addElement(selected);
}
else if (latents.contains(m.getSource())) {
drawingEdge = true;
selected = new WorkbenchEdge((Latent)m.getSource(), m.getPoint());
edges.addElement(selected);
}
}
// track the moving of variables so that we don't get flicker from
// the mouse exiting the variable by out-racing it.
else if (m.getSource() instanceof WBVariable) movingVariable = true;
else if (m.getSource() instanceof Latent) movingVariable = true;
break;
case ARROW: // only respond if it's on a Workbench variable or Latent
if (selected != null) {
((WorkbenchEdge)selected).setSelected(false);
selected = null;
repaint();
}
if (onWorkbench.contains(m.getSource())) {
drawingEdge = true;
selected = new WorkbenchEdge((WBVariable)m.getSource(), m.getPoint());
edges.addElement(selected);
}
else if (latents.contains(m.getSource())) {
drawingEdge = true;
selected = new WorkbenchEdge((Latent)m.getSource(), m.getPoint());
edges.addElement(selected);
}
break;
default: break;
}
return;
}
/**
* Currently does nothing
* @param m The MouseEvent to respond to
*/
public void mouseMoved(MouseEvent m) { m.consume(); }
/**
* Tells the WBVariable entered to highlight itself
* @param m The MouseEvent to respond to
*/
public void mouseEntered(MouseEvent m) {
// ignore, if we're moving a variable
if (movingVariable) return;
// Highlight the WBVariable
if (m.getSource() instanceof WBVariable) {
((WBVariable)m.getSource()).setHighlight(true);
((WBVariable)m.getSource()).repaint();
return;
}
// Highlight the Latent
if (m.getSource() instanceof Latent) {
((Latent)m.getSource()).setHighlight(true);
((Latent)m.getSource()).repaint();
return;
}
}
/**
* Tells the WBVariable exited to "un-highlight" itself
* @param m The MouseEvent to respond to
*/
public void mouseExited(MouseEvent m) {
// ignore, if we're moving a variable
if (movingVariable) return;
// Un-highlight the WBVariable
if (m.getSource() instanceof WBVariable) {
((WBVariable)m.getSource()).setHighlight(false);
((WBVariable)m.getSource()).repaint();
return;
}
// Un-highlight the Latent
if (m.getSource() instanceof Latent) {
((Latent)m.getSource()).setHighlight(false);
((Latent)m.getSource()).repaint();
return;
}
}
// MouseEvent helper methods
/**
* @return Whether the JInternalFrame is in a state to accept the
* event
*/
private boolean acceptEvent() {
// find the JInternalFrame containing the Workbench
Container p = getParent();
while (true) {
if (p == null) return true;
if (p instanceof JInternalFrame) break;
else p = p.getParent();
}
return ((JInternalFrame)p).isSelected();
}
/**
* Selects the edge passing through p, if there is one.
* @param p The Point to test on
*/
private void selectEdge(Point p) {
boolean needsRepaint = false;
if (selected != null) {
((WorkbenchEdge)selected).setSelected(false);
selected = null;
needsRepaint = true;
}
for (int i=edges.size()-1; i>=0; i--) {
WorkbenchEdge edge = (WorkbenchEdge)edges.elementAt(i);
if (edge.getSleeve().contains(p)) {
edge.setSelected(true);
selected = edge;
repaint();
return;
}
}
if (needsRepaint) repaint();
return;
}
/**
* Adds a latent variable at the indicated point.
* @param p The Point at which to add the Latent
*/
private void addLatent(Point p) {
Latent latent = new Latent("L"+String.valueOf(numCreatedLatents));
numCreatedLatents++;
latents.addElement(latent);
add(latent);
model.addLatent(latent.getName());
latent.setBounds(p.x, p.y, latent.getPreferredSize().width,
latent.getPreferredSize().height);
latent.addMouseListener(this);
latent.addMouseMotionListener(this);
latent.addPropertyChangeListener(this);
repaint();
return;
}
/**
* Removes the given latent variable.
* @param l The Latent to remove
*/
private void removeLatent(Latent l) {
latents.removeElement(l);
remove(l);
// remove all of the WorkbenchEdges that l is involved in
for (int i=edges.size()-1; i>=0; i--) {
WorkbenchEdge e = (WorkbenchEdge)edges.elementAt(i);
if ((e.getFromObject() == l) || (e.getToObject() == l))
edges.removeElement(e);
}
// and tell the model to fix the TetradGraph
model.removeLatent(l.getName());
l = null;
repaint();
return;
}
/**
* Adds a randomizer to the given WBVariable
* @param src The WBVariable we're adding a Randomizer to
*/
private void addRandomizer(WBVariable src) {
// first remove the lock on the WBVariable (if there is one)
if (src.isLocked()) {
String oldLockValue = src.getActiveValue();
src.setLocked(false);
src.setActiveValue(null);
firePropertyChange("lock:"+src.getName(), oldLockValue, null);
src.setSize(src.getPreferredSize());
src.repaint();
}
// then add the randomizer
Randomizer random = new Randomizer(src);
random.setCodebase(codebase);
randomizers.addElement(random);
src.setRandomizer(random);
add(random);
Rectangle srcBounds = src.getBounds();
random.setBounds(srcBounds.x+srcBounds.width+10, srcBounds.y,
random.getPreferredSize().width,
random.getPreferredSize().height);
random.addMouseListener(this);
random.addMouseMotionListener(this);
random.addPropertyChangeListener(this);
repaint();
firePropertyChange("randomized:"+src.getName(), false, true);
return;
}
/**
* Figures out the new value for the lock, and then calls the set method.
* @param src The WBVariable to lock
*/
private void getAndSetLock(WBVariable src) {
// figure out the old state
String oldLockValue = src.getActiveValue();
String preset = (oldLockValue == null) ? "Unlocked" : oldLockValue;
// get the list of values
String[] shortValues = src.getValues();
String[] values = new String[shortValues.length+1];
System.arraycopy(shortValues, 0, values, 0, shortValues.length);
values[shortValues.length] = "Unlocked";
// get the new value
String newValue = (String)JOptionPane.showInternalInputDialog(this,
"Set the lock value:", "Lock value",
JOptionPane.QUESTION_MESSAGE, null, values, preset);
// if they chose "Cancel", return
if (newValue == null) return;
// if they choose the same thing, then return
else if (newValue.equals(preset)) return;
// otherwise, set it to the appropriate value
else if (newValue.equals("Unlocked")) setLock(src, null);
else setLock(src, newValue);
if (getParent() instanceof JInternalFrame) getParent().requestFocus();
}
/**
* This method does the locking of a WBVariable
* @param src The WBVariable to lock
* @param newValue The locking value of the WBVariable
*/
private void setLock(WBVariable src, String newValue) {
String oldLockValue = src.getActiveValue();
// remove the randomizer (if any), and set the lock
if (src.isRandomized()) {
Randomizer rand = src.getRandomizer();
randomizers.removeElement(rand);
remove((Component)rand);
src.setRandomizer(null);
firePropertyChange("randomized:"+src.getName(), true, false);
}
if (newValue == null) {
src.setLocked(false);
src.setActiveValue(null);
firePropertyChange("lock:"+src.getName(), oldLockValue, null);
}
else {
src.setLocked(true);
src.setActiveValue(newValue);
firePropertyChange("lock:"+src.getName(), oldLockValue, newValue);
}
src.setSize(src.getPreferredSize());
src.repaint();
repaint();
}
/**
* Returns a Point indicating the closest Workbench spot to the Component's
* current location (so we can move it back onto the Workbench).
* @param src The Component to check
* @return The nearest Point on the Workbench
*/
private Point moveOnScreen(Component src) {
Rectangle bounds = src.getBounds();
Dimension size = getSize();
Insets insets = getInsets();
FontMetrics fm = getFontMetrics(getFont());
if (bounds.x < insets.left+WB_MARGIN.left)
bounds.x = insets.left+WB_MARGIN.left;
if (bounds.y < insets.top+WB_MARGIN.top+fm.getHeight())
bounds.y = insets.top+WB_MARGIN.top+fm.getHeight();
if (bounds.x+bounds.width > size.width-insets.right-WB_MARGIN.right)
bounds.x = size.width-insets.right-WB_MARGIN.right-bounds.width;
if (bounds.y+bounds.height > size.height-insets.bottom-BIN_MARGIN.bottom)
bounds.y =
size.height-bounds.height-insets.bottom-BIN_MARGIN.bottom;
return new Point(bounds.x, bounds.y);
}
/**
* Sets the given WBVariable either on or off the workbench
* @param src The WBVariable to set
* @param on true
, if the WBVariable is now on the workbench;
* false
, if not.
*/
private void setOnWorkbench(WBVariable src, boolean on) {
boolean previouslyOn = false;
if (!(on)) {
// if it's on the Workbench, take it off
if (onWorkbench.contains(src)) {
previouslyOn = true;
onWorkbench.removeElement(src);
// unlock
if (src.isLocked()) {
String oldVal = src.getActiveValue();
src.setLocked(false);
src.setActiveValue(null);
firePropertyChange("lock:"+src.getName(), oldVal, null);
}
// pass through the WorkbenchEdges to find the ones to remove
for (int i=edges.size()-1; i>=0; i--) {
WorkbenchEdge check = (WorkbenchEdge)edges.elementAt(i);
if ((src == check.getFromObject()) ||
(src == check.getToObject())) {
edges.removeElement(check);
removeEdgeFromTetradGraph(check);
}
}
if (src.isRandomized()) {
Randomizer rand = src.getRandomizer();
randomizers.removeElement(rand);
remove(rand);
src.setRandomizer(null);
}
}
// set the bin location by figuring out which var it is, and
// moving it there (using it's "unique" location)
int i; for (i=0; itrue, if the edge was completed;
* false
, if not
*/
private boolean finishEdge(Point p) {
// check to see if any of the Workbench vars is the target
WBVariable target;
for (int i=0; i=0; i--) {
// figure out which variable it is
String colName = tm.getColumnName(i);
int j; for (j=0; j=0; i--) {
removeLatent((Latent)latents.elementAt(i));
}
// First, deal with the WBVariables
for (int i=0; i=0; i--) {
edges.addElement(edgesToLoad.elementAt(i));
addEdgeToTetradGraph((WorkbenchEdge)edgesToLoad.elementAt(i));
}
// Finally, add the var-to-latent and latent-to-var edges
Vector lEdgesToLoad = ss.getLatentEdgesVector();
for (int i=lEdgesToLoad.size()-1; i>=0; i--) {
StringTokenizer st = new
StringTokenizer((String)lEdgesToLoad.elementAt(i), "->");
String from = st.nextToken();
String to = st.nextToken();
// find the fromObject
WorkbenchObject fromObj = null;
if (from.startsWith(ScreenShot.LATENT)) {
String newFrom = from.substring(ScreenShot.LATENT.length());
for (int j=latents.size()-1; j>=0; j--) {
if (newFrom.equals(((Latent)latents.elementAt(j)).getName())) {
fromObj = (WorkbenchObject)latents.elementAt(j);
break;
}
}
}
else {
for (int j=0; j=0; j--) {
if (newTo.equals(((Latent)latents.elementAt(j)).getName())) {
toObj = (WorkbenchObject)latents.elementAt(j);
break;
}
}
}
else {
for (int j=0; j=0; i--) {
((WorkbenchEdge)edges.elementAt(i)).setEnabled(b);
}
for (int i=0; i=0; i--) {
((Latent)latents.elementAt(i)).setExperimenting(!(b));
}
for (int i=randomizers.size()-1; i>=0; i--) {
((Randomizer)randomizers.elementAt(i)).setExperimenting(!(b));
}
repaint();
}
/**
* This method is an accessor method, since we're storing the ScreenShots in
* the workbench.
* @param index The index of the ScreenShot to load
*/
public void loadScreenShot(int index) {
loadScreenShot((ScreenShot)screenShots.elementAt(index));
}
/**
* @param The Cursor for both the Workbench and its WBVariables
*/
public void changeCursor(Cursor cursor) {
setCursor(cursor);
for (int i=0; i=0; i--) {
if (((WBVariable)onWorkbench.elementAt(i)).isLocked())
locks.addElement(onWorkbench.elementAt(i));
}
String[] locked = new String[locks.size()];
for (int i=0; i=0; i--) {
rands[i] =
((Randomizer)randomizers.elementAt(i)).getTarget().getName();
}
return rands;
}
/**
* @return An Image of the area of the Workbench with either variables or
* randomizers on it. If we're in the hypothesize_predict mode,
* then the "broken" arrows (because of locks or randomizers)
* have X's through them.
*/
public Image getWorkbenchImage() { return getWorkbenchImage(false); }
/**
* @return An Image of the area of the Workbench with either variables or
* randomizers on it, but without any edges.
*/
public Image getExperimentalSetupImage() {
return getWorkbenchImage(true);
}
/**
* This is a private method so that we can use the same code to draw the
* experimental setups and the workbench images.
*
* @param expSetup Whether we're drawing the experimental setup
* @return An Image of the relevant Workbench area
*/
private Image getWorkbenchImage(boolean expSetup) {
if (onWorkbench.size() == 0) return null;
// we need to figure out the bounding rectangle for the Image
int x1 = getWidth();
int y1 = getHeight(); // the defaults are backwards because we want
int x2 = 0; // to be able to use Math.min and Math.max
int y2 = 0;
Vector rects = new Vector();
// pass through the variables
for (int i=onWorkbench.size()-1; i>=0; i--) {
Rectangle bounds = ((WBVariable)onWorkbench.elementAt(i)).getBounds();
x1 = Math.min(x1, bounds.x);
y1 = Math.min(y1, bounds.y);
x2 = Math.max(x2, bounds.x+bounds.width);
y2 = Math.max(y2, bounds.y+bounds.height);
rects.add(bounds.clone());
}
// pass through the randomizers
for (int i=randomizers.size()-1; i>=0; i--) {
Rectangle bounds =
((Randomizer)randomizers.elementAt(i)).getBounds();
x1 = Math.min(x1, bounds.x);
y1 = Math.min(y1, bounds.y);
x2 = Math.max(x2, bounds.x+bounds.width);
y2 = Math.max(y2, bounds.y+bounds.height);
rects.add(bounds.clone());
}
// pass through the latents, if we're not in experimental setup
if (!expSetup) {
for (int i=latents.size()-1; i>=0; i--) {
Rectangle bounds = ((Latent)latents.elementAt(i)).getBounds();
x1 = Math.min(x1, bounds.x);
y1 = Math.min(y1, bounds.y);
x2 = Math.max(x2, bounds.x+bounds.width);
y2 = Math.max(y2, bounds.y+bounds.height);
rects.add(bounds.clone());
}
}
Point origin = new Point(x1-1, y1-1);
// start the scaling...first, translate all rects so origin=(0,0)
int[][] shift_len = new int[rects.size()][2];
for (int i=rects.size()-1; i>=0; i--) {
Rectangle r = (Rectangle)rects.elementAt(i);
r.translate(-origin.x, -origin.y);
shift_len[i][0] = -r.x;
shift_len[i][1] = -r.y;
}
// now, determine how much we can bring in the variables
int width = x2-x1+2;
int height = y2-y1+2;
int n = 1; double mult = 1.0;
int MIN_SHIFT = 5; int MIN_DIST = 40;
while ((width/(1< MIN_SHIFT) && (height/(1< MIN_SHIFT)) {
// first, proportionally translate all of the rects in
for (int i=rects.size()-1; i>=0; i--) {
Rectangle r = (Rectangle)rects.elementAt(i);
r.translate(shift_len[i][0]/(1<0; i--) {
for (int j=i-1; j>=0; j--) {
Rectangle r1 = (Rectangle)rects.elementAt(i);
Rectangle r2 = (Rectangle)rects.elementAt(j);
Rectangle exR1 = new Rectangle(r1.x-MIN_DIST/2,
r1.y-MIN_DIST/2, r1.width+MIN_DIST,
r1.height+MIN_DIST);
Rectangle exR2 = new Rectangle(r2.x-MIN_DIST/2,
r2.y-MIN_DIST/2, r2.width+MIN_DIST,
r2.height+MIN_DIST);
if (exR1.intersects(exR2)) {
tooClose = true;
break;
}
}
if (tooClose) break;
}
// if any were too close, then translate back
if (tooClose) {
for (int i=rects.size()-1; i>=0; i--) {
Rectangle r = (Rectangle)rects.elementAt(i);
r.translate(-shift_len[i][0]/(1<=0; i--) {
Rectangle r = (Rectangle)rects.elementAt(i);
xMax = Math.max(xMax, r.x+r.width);
yMax = Math.max(yMax, r.y+r.height);
}
width = xMax+1; height = yMax+1;
// now, create the new Image, and pass the Graphics context to the ui
Image benchIm = createImage(width, height);
Graphics g = benchIm.getGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, width, height);
((WorkbenchUI)ui).drawWorkbenchImage(g, origin, mult,
expSetup, this);
return benchIm;
}
/**
* @return A deep clone of the edges vector
*/
public Vector getClonedEdgesVector() {
Vector cl = new Vector(edges.size());
for (int i=0; itrue, since the Workbench is opaque
*/
public boolean isOpaque() { return true; }
/**
* @return The margin around the inside edge of the workbench
*/
public Insets getWorkbenchMargin() { return WB_MARGIN; }
/**
* @return The margin around the inside edge of the variable bin
*/
public Insets getBinMargin() { return BIN_MARGIN; }
/**
* @return The width of the divider between the workbench and variable bin
*/
public int getBinDividerWidth() { return BIN_DIVIDER_WIDTH; }
/**
* @return The text label for the variable bin
*/
public String getBinLabel() { return BIN_LABEL; }
/**
* @return The text label for the workbench
*/
public String getWorkbenchLabel() { return WORKBENCH_LABEL; }
/**
* @param index The index of the WBVariable
* @param icon The icon for the WBVariable
*/
public void setVariableIcon(int index, Icon icon) {
variables[index].setNameIcon(icon);
}
/**
* @param name The name of the WBVariable
* @param icon The icon for the WBVariable
*/
public void setVariableIcon(String name, Icon icon) {
for (int i=0; i
insets.left+size.width) ?
insets.left+size.width-oldBounds.width : oldBounds.x;
oldBounds.y = (oldBounds.y+oldBounds.height >
insets.top+size.height-BIN_HEIGHT) ?
insets.top+size.height-BIN_HEIGHT-oldBounds.height :
oldBounds.y;
variables[i].setBounds(oldBounds);
}
// otherwise, we need to place it properly in the bin
else {
variables[i].setBounds(bin_rect.x+BIN_MARGIN.left+var_width*i,
bin_rect.y+BIN_MARGIN.top, var_width-VARIABLE_SEPARATION,
variables[0].getPreferredSize().height);
}
}
// lay out the randomizers
for (int i=randomizers.size()-1; i>=0; i--) {
Rectangle oldBounds =
((Randomizer)randomizers.elementAt(i)).getBounds();
oldBounds.x = (oldBounds.x < insets.left+WB_MARGIN.left) ?
insets.left+WB_MARGIN.left : oldBounds.x;
oldBounds.y = (oldBounds.y < insets.top+WB_MARGIN.top) ?
insets.top+WB_MARGIN.top : oldBounds.y;
oldBounds.x = (oldBounds.x+oldBounds.width >
insets.left+size.width) ?
insets.left+size.width-oldBounds.width : oldBounds.x;
oldBounds.y = (oldBounds.y+oldBounds.height >
insets.top+size.height-BIN_HEIGHT) ?
insets.top+size.height-BIN_HEIGHT-oldBounds.height :
oldBounds.y;
((Randomizer)randomizers.elementAt(i)).setBounds(oldBounds);
}
// lay out the latents
for (int i=latents.size()-1; i>=0; i--) {
Rectangle oldBounds = ((Latent)latents.elementAt(i)).getBounds();
oldBounds.x = (oldBounds.x < insets.left+WB_MARGIN.left) ?
insets.left+WB_MARGIN.left : oldBounds.x;
oldBounds.y = (oldBounds.y < insets.top+WB_MARGIN.top) ?
insets.top+WB_MARGIN.top : oldBounds.y;
oldBounds.x = (oldBounds.x+oldBounds.width >
insets.left+size.width) ?
insets.left+size.width-oldBounds.width : oldBounds.x;
oldBounds.y = (oldBounds.y+oldBounds.height >
insets.top+size.height-BIN_HEIGHT) ?
insets.top+size.height-BIN_HEIGHT-oldBounds.height :
oldBounds.y;
((Latent)latents.elementAt(i)).setBounds(oldBounds);
}
}
/**
* @param parent The parent of the Workbench
* @return The preferred size of the Workbench
*/
public Dimension preferredLayoutSize(Container parent) {
return getPreferredSize();
}
/**
* @param parent The parent of the Workbench
* @return The minimum size of the Workbench
*/
public Dimension minimumLayoutSize(Container parent) {
return getMinimumSize();
}
/**
* Currently does nothing.
* @param name The name of the Component
* @param comp The Component to be added
*/
public void addLayoutComponent(String name, Component comp) { ; }
/**
* Currently does nothing
* @param comp The Component to remove
*/
public void removeLayoutComponent(Component comp) { ; }
// Model and WBVariable accessor methods
/**
* @return A Vector of the WorkbenchEdges
*/
public Vector getEdges() { return edges; }
/**
* @return A Vector of the Latents
*/
public Vector getLatents() { return latents; }
/**
* @return A Vector of the Randomizers
*/
public Vector getRandomizers() { return randomizers; }
/**
* @return The user-constructed TetradGraph, with edges into locked and
* randomized variables removed
*/
public TetradGraph getUserManipulatedTetradGraph() {
return applySetupToTetradGraph(
(TetradGraph)model.getTetradGraph().clone());
}
/**
* @return The current manipulated DiGraph for the underlying BayesNet.
*/
public TetradGraph getTrueManipulatedTetradGraph() {
TetradGraph trueGraph = (TetradGraph)model.getBayesNet().getGraph();
return applySetupToTetradGraph((TetradGraph)trueGraph.clone());
}
/**
* Applies the current experimental setup to the given TetradGraph.
* @param tg The TetradGraph to be manipulated
* @return The adjusted TetradGraph
*/
private TetradGraph applySetupToTetradGraph(TetradGraph tg) {
for (int i=0; iindex-th WBVariable
*/
public WBVariable getVariable(int index) { return variables[index]; }
/**
* @param newVars The new array of WBVariables
*/
public void setVariables(WBVariable[] newVars) {
if (newVars != variables) {
if (newVars == null)
newVars = createVariables();
variables = newVars;
firePropertyChange("allvariables", null, null);
}
}
/**
* @param variable The new WBVariable
* @param index The index of the new WBVariable
*/
public void setVariable(WBVariable variable, int index) {
if (variable != variables[index]) {
if (variable == null)
variable = new WBVariable();
String oldName = variables[index].getName();
variables[index] = variable;
firePropertyChange("variable"+index, oldName, variable.getName());
}
}
/**
* @return The current Workbench mode
*/
public int getMode() { return model.getMode(); }
/**
* @param newMode The new Workbench mode
*/
public void setMode(int newMode) {
if (newMode != model.getMode()) {
int oldMode = model.getMode();
model.setMode(newMode);
// deal with the mode switching changes
if (selected != null) {
if (selected instanceof WorkbenchEdge)
((WorkbenchEdge)selected).setSelected(false);
else if (selected instanceof Randomizer)
((Randomizer)selected).setSelected(false);
selected = null;
}
boolean b = (newMode == HYPOTHESIZE_PREDICT_MODE);
for (int i=edges.size()-1; i>=0; i--) {
((WorkbenchEdge)edges.elementAt(i)).setEnabled(b);
}
for (int i=0; i=0; i--) {
((Latent)latents.elementAt(i)).setExperimenting(!(b));
}
for (int i=randomizers.size()-1; i>=0; i--) {
((Randomizer)randomizers.elementAt(i)).setExperimenting(!(b));
}
repaint();
firePropertyChange("mode", oldMode, newMode);
}
}
/**
* @return The current Workbench tool
*/
public int getTool() { return model.getTool(); }
/**
* @param newTool The new Workbench tool
*/
public void setTool(int newTool) {
if (newTool != model.getTool()) {
int oldTool = model.getTool();
// there might be something selected
if (selected != null) {
if (selected instanceof WorkbenchEdge)
((WorkbenchEdge)selected).setSelected(false);
else if (selected instanceof Randomizer)
((Randomizer)selected).setSelected(false);
selected = null;
repaint();
}
model.setTool(newTool);
firePropertyChange("tool", oldTool, newTool);
}
}
// UI methods
/**
* @param ui The WorkbenchUI to attach to this Workbench
*/
public void setUI(WorkbenchUI ui) { super.setUI(ui); }
/**
* Sets up the UI appropriately
*/
public void updateUI() {
setUI((WorkbenchUI)UIManager.getUI(this));
invalidate();
}
/**
* @return The String identifying the UI for this Class
*/
public String getUIClassID() { return UIClassID; }
// Helper methods
/**
* Creates and initializes the WBVariables using the underlying BayesNet
* @return The array of WBVariables to use
*/
private WBVariable[] createVariables() {
BayesNetIM bn = model.getBayesNet();
WBVariable[] retVars = new WBVariable[bn.getGraph().getNumNodes()];
Iterator it = model.getBayesNet().nodeIterator();
int i=0;
while (it.hasNext()) {
Node node = (Node)it.next();
Vector vals = model.getBayesNet().getValue(node);
String[] valArray = new String[vals.size()];
for (int j=0; j