content: package edu.cmu.hcii.whyline.ui;
  
  import java.awt.Dimension;
  import java.awt.Toolkit;
  import java.awt.event.ActionEvent;
  
  import javax.swing.AbstractAction;
  import javax.swing.Action;
  import javax.swing.tree.TreePath;
  
  import edu.cmu.hcii.whyline.qa.Explanation;
  import edu.cmu.hcii.whyline.qa.Question;
  import edu.cmu.hcii.whyline.qa.WhyDidEventOccur;
  import edu.cmu.hcii.whyline.trace.Trace;
  import edu.cmu.hcii.whyline.trace.nodes.*;
  import edu.cmu.hcii.whyline.ui.components.HeadlinedTreePanel;
  import edu.cmu.hcii.whyline.ui.components.WhylineButton;
  
  /**
   * @author Andrew J. Ko
   *
   */
  public final class ObjectsUI extends HeadlinedTreePanel<ObjectState> {
  
  	private final Action explainAction = new AbstractAction("explain") {
  		public void actionPerformed(ActionEvent e) {
  			DynamicNode<?> node = getSelectedDynamicNode();
  			if(node != null) {
  				int eventID = node.getAssociatedEventID();
  				Question<?> q = new WhyDidEventOccur(whylineUI, eventID, getTrace().getDescription(eventID));
  				whylineUI.answer(q);
  			}
  		}
  	};
  
  	private final Action removeAction = new AbstractAction("" + UI.MINUS) {
  		public void actionPerformed(ActionEvent e) {
  			whylineUI.getObjectsUI().removeObject(getSelectedObjectID());
  			setEnabled(false);
  		}
  	};
  
  	private final Action previousAssignment = new AbstractAction("previous =") {
  		public void actionPerformed(ActionEvent e) {
  
  			Explanation explanation = getExplanationOfPreviousOrNextFieldAssignment(false);
  			if(explanation == null) Toolkit.getDefaultToolkit().beep();
  			else	whylineUI.selectExplanation(explanation,true, "objects");
  
  		}
  	};
  
  	private final Action nextAssignment = new AbstractAction("next =") {
  		public void actionPerformed(ActionEvent e) {
  
  			Explanation explanation = getExplanationOfPreviousOrNextFieldAssignment(true);
  			if(explanation == null) Toolkit.getDefaultToolkit().beep();
  			else	whylineUI.selectExplanation(explanation,true, "objects");
  			
  		}
  	};
  
  	public ObjectsUI(WhylineUI whylineUI) {
  	
  		super("objects", whylineUI);
  		
  		setMinimumSize(new Dimension(UI.getDefaultInfoPaneWidth(whylineUI), 0));
  		setPreferredSize(new Dimension(UI.getDefaultInfoPaneWidth(whylineUI), UI.getDefaultInfoPaneHeight(whylineUI)));
  
  		removeAction.setEnabled(false);
  		
  		addTool(new WhylineButton(removeAction, "remove the selected object from the watch list"));
  		if(whylineUI.isWhyline()) {
  			addTool(new WhylineButton(explainAction, "explain why the selected field has its value"));
  			addTool(new WhylineButton(previousAssignment, "find the previous assignment to this field"));
  			addTool(new WhylineButton(nextAssignment, "find the next assignment to this field"));
  		}
  		
  	}
  
  	private Explanation getExplanationOfPreviousOrNextFieldAssignment(boolean next) {
  		
  		if(!(getSelectedDynamicNode() instanceof FieldState))
  			return null;
  		FieldState field = (FieldState)getSelectedDynamicNode();
  		int eventID = field.getCurrentEventID();
  		int assignmentID = 
  			next ?
  				getTrace().findFieldAssignmentAfter(field.getField(), field.getObjectID(), eventID + 1) :
  				getTrace().findFieldAssignmentBefore(field.getField(), field.getObjectID(), eventID - 1);
  		if(assignmentID == eventID || assignmentID < 0)
  			return null;
  		else
  			return whylineUI.getVisualizationUIVisible().getAnswer().getExplanationFor(assignmentID);
  		
  	}
  	
  	public boolean isWatching(long objectID) { return getRoot().getChildren().contains(getTrace().getObjectNode(objectID)); }
  	
  	protected void handleSelection() {
  		
  		Node<?> selection = getSelectedNode(); 
  					
  		long objectID = getSelectedObjectID();
  		removeAction.setEnabled(objectID > 0);
  		
  		if(whylineUI.isWhyline()) {
  		
  			DynamicNode<?> node = getSelectedDynamicNode();
  			explainAction.setEnabled(node != null && node.getAssociatedEventID() > 0);
  	
  			if(objectID > 0) whylineUI.getGraphicsUI().setHighlight(whylineUI.getTrace().getObjectNode(objectID));
  			
  			previousAssignment.setEnabled(node instanceof FieldState);
  			nextAssignment.setEnabled(node instanceof FieldState);
  			
  		}
  
  	}
  
  	public long getSelectedObjectID() {
  		
  		TreePath path = getSelectionPath();
  		if(path== null || path.getPathCount() <= 1) return 0;
  		Object selection = path.getPathComponent(1);
  		return selection instanceof ReferenceState ? ((ReferenceState)selection).getObjectIDForChildren() : 0;
  		
  	}
  
  	public void showEventID(int eventID) {
  		
  		if(!whylineUI.isDynamicInfoShowing()) return;
  
  		for(ObjectState obj : getRoot().getChildren())
  			obj.propagateCurrentEventID(eventID);
  
  		DynamicNode<?> state = getSelectedDynamicNode();
  		if(state != null) state.update();
  		
  		updateTree();
  		
  	}
  	
  	public void addObject(long objectID) {
  
  		ObjectState obj = getTrace().getObjectNode(objectID);
  		getRoot().addChild(obj);
  		updateTree();
  
  		whylineUI.getPersistentState().addRelevantObject(objectID);
  
  	}
  	
  	public void removeObject(long objectID) {
  		
  		getRoot().removeChild(getTrace().getObjectNode(objectID));
  		updateTree();
  
  		whylineUI.getPersistentState().removeRelevantObject(objectID);
  		
  	}
  
  	public void removeAllObjects() {
  
  		getRoot().resetChildren();
  		updateTree();
  		
  	}
  
  	protected boolean isLeaf(Node<?> node) { return node instanceof FieldState; }
  
  	public Trace getTrace() { return whylineUI.getTrace(); }
  
  	protected void handleExpansion() {
  
  		if(getSelectedDynamicNode() instanceof ReferenceState) {
  			ReferenceState ref = (ReferenceState)getSelectedDynamicNode();
  			
  			for(ReferenceState node : ref.getChildren())
  				node.propagateCurrentEventID(ref.getCurrentEventID());
  			
  		}
  
  	}
  
  }