package de.unisiegen.tpml.graphics.smallstep;
import java.awt.AWTEvent;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.text.MessageFormat;
import javax.swing.JComponent;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;
import de.unisiegen.tpml.core.ProofGuessException;
import de.unisiegen.tpml.core.ProofNode;
import de.unisiegen.tpml.core.ProofRule;
import de.unisiegen.tpml.core.ProofStep;
import de.unisiegen.tpml.core.expressions.Expression;
import de.unisiegen.tpml.core.expressions.Location;
import de.unisiegen.tpml.core.languages.Language;
import de.unisiegen.tpml.core.languages.LanguageTranslator;
import de.unisiegen.tpml.core.smallstep.SmallStepProofModel;
import de.unisiegen.tpml.core.smallstep.SmallStepProofNode;
import de.unisiegen.tpml.graphics.Messages;
import de.unisiegen.tpml.graphics.components.CompoundExpression;
import de.unisiegen.tpml.graphics.components.MenuButton;
import de.unisiegen.tpml.graphics.components.MenuButtonListener;
import de.unisiegen.tpml.graphics.components.MenuGuessItem;
import de.unisiegen.tpml.graphics.components.MenuGuessTreeItem;
import de.unisiegen.tpml.graphics.components.MenuRuleItem;
import de.unisiegen.tpml.graphics.components.MenuTranslateItem;
import de.unisiegen.tpml.graphics.components.RulesMenu;
import de.unisiegen.tpml.graphics.outline.listener.OutlineMouseListener;
/**
* The graphical representation of a
* {@link de.unisiegen.tpml.core.smallstep.SmallStepProofNode}.<br>
* <br>
* The <code>SmallStepNodeComponent</code> is a bit more complicated than the
* node from the other two GUIs because the rules applied to this node are
* pointing on the expression of the next node.<br>
* To handle this right, <b>one</b> <code>SmallStepNodeComponent</code> is
* build like the following image shows:<br>
* <img src="../../../../../../images/smallstepnode.png" /><br>
* <br>
* Actualy there are only two elements to handle here (that is they are handled
* in the
* {@link de.unisiegen.tpml.graphics.smallstep.SmallStepComponent#placeNode(SmallStepProofNode, int, int)}-method).
* Those two elements are the {@link #compoundExpression} at the top right and
* the {@link #rules} at the bottom left. <img
* src="../../../../../../images/smallstepnode_scheme.png" /><br>
* <br>
* Because the elements of this node need to get layed out in an arrangement
* with the elements of the parent-node and the child-node, both components have
* a dimension containing the size they need for themeself and an additional
* information on the actual size they need to fill(this is alway more or equal
* as the size of itself). The <code>rules</code> of the parent node needs to
* get verticaly centered and aligned with the <code>expression</code> of this
* node. Usualy the <code>rules</code> are higher than the
* <code>expression</code>s. So the height of the
* {@link #expressionDimension} is less than the {@link #actualExpressionHeight}.
* When the <code>expression</code> gets placed the
* <code>actualExpressionHeight</code> is used for the height. The
* {@link #actualRuleHeight} of the parent node than is the same as this
* <code>actualExpressionHeight</code>. <br>
* Analog the same is done for the <code>rules</code> of this node and the
* <code>expression</code> of the child-node. The only difference is: when the
* <code>rules</code> are placed theire vertical-center-alignment needs to get
* calculated menualy, because the
* {@link de.unisiegen.tpml.graphics.smallstep.SmallStepRulesComponent} is
* always top-aligned.<br>
*
* @author Marcell Fischbach
* @author Benedikt Meurer
* @author Michael Oeste
* @version $Rev$
* @see de.unisiegen.tpml.graphics.smallstep.SmallStepView
* @see de.unisiegen.tpml.graphics.smallstep.SmallStepComponent
* @see de.unisiegen.tpml.graphics.smallstep.SmallStepRulesComponent
* @see de.unisiegen.tpml.graphics.smallstep.SmallStepRuleLabel
* @see CompoundExpression
*/
public class SmallStepNodeComponent extends JComponent
{
/**
*
*/
private static final long serialVersionUID = 5536947349690384851L;
/**
* The origin {@link SmallStepProofNode}
*/
private SmallStepProofNode proofNode;
/**
* The {@link SmallStepProofModel}
*/
private SmallStepProofModel proofModel;
/**
* The {@link SmallStepRulesComponent} containing the Labels of already
* evaluated Rules and the MenuButton for Rules not yet evaluated.
*/
private SmallStepRulesComponent rules;
/**
* The {@link Dimension} the rules needs to get drawn correctly.
*/
private Dimension ruleDimension;
/**
* The actual height the rules should use. This is >= ruleDimension.height.
* This height is determined by the expression height of the child node.
*/
private int actualRuleHeight;
/**
* The Compound expression that is used to display the expression.
*/
CompoundExpression<Location, Expression> compoundExpression;
/**
* The {@link Dimension} the expression needs to get drawn correctly.
*/
private Dimension expressionDimension;
/**
* The actual height the expression should use. This is >=
* expressionDimension.height. This height is determined by the rule height of
* the parent node.
*/
private int actualExpressionHeight;
/**
* The top-left-most position where the entire node should located.
*/
private Point origin;
/**
* Flags that contains whether the expression has memory.
*/
private boolean memoryEnabled;
/**
* The free space in pixels that should be hold between the components
*/
private int spacing;
/**
* Entry within the context menu. Need to hold this value because is value can
* change. This item is enabled/disable in the {@link #update()}-Method.
*
* @see #update()
*/
private MenuTranslateItem translateItem;
/**
* Translator that is used to determine whether the expression contains
* syntactical sugar.
*/
private LanguageTranslator translator;
/**
* The expression that should be underlined. When the user moves the mouse
* over a rule (not not grouped rules) or the MenuButton, that a part of the
* entire expressions needs to get underlined.
*/
private Expression currentUnderlineExpression;
/**
* Adapter that is used to get added to every object that can cause the gui to
* underline an expression. It is bound to every {@link SmallStepRuleLabel}
* and to every {@link MenuButton}.
*/
private MouseMotionAdapter underlineRuleAdapter;
/**
* containing the rules it may contain submenus if to many (set in TOMANY)
* rules are in the popupmenu
*/
private JPopupMenu menu;
/**
* The Manager for teh RulesMenus
*/
private RulesMenu rulesMenu = new RulesMenu();
/**
* Used internaly. When the underlining is cleared it will be done recursively
* over the entire tree. It needs to be done in two times one time directed to
* the parent and one time directed to the children. This enum is used by
* {@link SmallStepNodeComponent#freeUnderliningSibling(boolean, Direction)};
*
* @author marcell
*/
private enum Direction {
/***/
DIRECTION_PARENT,
/***/
DIRECTION_CHILD,
}
private boolean advanced ;
/**
* Constructs a SmallStepNodeComponent.<br>
* <br>
* All objects needed for one SmallStepNodeComponent are created and added to
* the {@link JComponent}.
*
* @param pProofNode The origin node from the model.
* @param pProofModel The model.
* @param pTranslator The translator that should be used to determine whether
* the Expression of this node contains syntactical sugar.
* @param pSpacing The spacing between the elements of the node.
* @param advanced Whether the small step view operates in advanced or
* beginner mode.
*/
public SmallStepNodeComponent (SmallStepProofNode pProofNode, SmallStepProofModel pProofModel,
LanguageTranslator pTranslator, int pSpacing, boolean advanced)
{
super();
this.proofNode = pProofNode;
this.proofModel = pProofModel;
this.translator = pTranslator;
this.advanced = advanced ;
this.currentUnderlineExpression = null;
// the dimension for the rules initialy (0, 0)
this.ruleDimension = new Dimension(0, 0);
// the dimension for the expression initialy (0, 0)
this.expressionDimension = new Dimension(0, 0);
this.compoundExpression = new CompoundExpression<Location, Expression>();
this.compoundExpression.addMouseListener(new OutlineMouseListener(this));
add(this.compoundExpression);
this.rules = new SmallStepRulesComponent(pProofNode);
add(this.rules);
this.memoryEnabled = this.proofModel.isMemoryEnabled();
this.spacing = 10;
this.translateItem = new MenuTranslateItem();
enableEvents(AWTEvent.MOUSE_EVENT_MASK);
this.rules.getMenuButton().addMenuButtonListener(new MenuButtonListener() {
public void menuClosed(MenuButton source)
{
// empty
}
public void menuItemActivated(MenuButton source, final JMenuItem item)
{
// setup a wait cursor for the toplevel ancestor
final Container toplevel = getTopLevelAncestor();
final Cursor cursor = toplevel.getCursor();
toplevel.setCursor(new Cursor(Cursor.WAIT_CURSOR));
// avoid blocking the popup menu
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
// handle the menu action
SmallStepNodeComponent.this.handleMenuActivated(item);
// wait for the repaint before resetting the cursor
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
// reset the cursor
toplevel.setCursor(cursor);
}
});
}
});
}
});
// create the adapters that will be used to determine
// whether an expression needs to get underlined
MouseMotionAdapter underlineThisAdapter = new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent event)
{
SmallStepNodeComponent.this.updateUnderlineExpression((Expression) null);
}
};
this.underlineRuleAdapter = new MouseMotionAdapter() {
@Override
public void mouseMoved(MouseEvent event)
{
// System.out.println(" Event: "+event);
// System.out.println("Typ: "+event.getSource());
// System.out.println("Position "+event.getX() +", "+ event.getY());
if (event.getSource() instanceof SmallStepRuleLabel)
{
SmallStepRuleLabel label = (SmallStepRuleLabel) event.getSource();
SmallStepNodeComponent.this.updateUnderlineExpression(label);
}
else if (event.getSource() instanceof MenuButton)
{
MenuButton button = (MenuButton) event.getSource();
SmallStepNodeComponent.this.updateUnderlineExpression(button);
}
else
{
SmallStepNodeComponent.this.compoundExpression.repaint();
}
}
};
this.addMouseMotionListener(underlineThisAdapter);
this.compoundExpression.addMouseMotionListener(underlineThisAdapter);
this.compoundExpression.addMouseMotionListener(this.underlineRuleAdapter);
this.rules.getMenuButton().addMouseMotionListener(this.underlineRuleAdapter);
this.addMouseMotionListener(underlineThisAdapter);
// apply the advanced setting
setAdvanced(advanced);
}
/**
* Just paints a Rect arround the silly {@link CompoundExpression} to see the overlapping
*/
@Override
protected void paintComponent(Graphics gc)
{
// TODO just recomment these two lines to see the Components
// gc.setColor(Color.black);
// gc.drawRect(0, 0, getWidth()-1, getHeight()-1);
super.paintComponent(gc);
}
// workaround: the single CompoundExpressions overlap the others. Thus it is not possible to
// react on MouseEvents so they must be handed out to the other CompoundExpressions
// WORKAROUND: START
@Override
protected void processMouseEvent(MouseEvent e)
{
// let this component handle the event first
super.processMouseEvent(e);
try
{
// check if we have a next SmallStepProofNode
ProofNode node = this.proofNode.getChildAt(0);
// determine the SmallStepNodeComponent for the next proof node
SmallStepNodeComponent nextComponent = (SmallStepNodeComponent) node.getUserObject();
if (nextComponent != null)
{
// translate x/y to world coordinates
int x = e.getX() + getX();
int y = e.getY() + getY();
// translate x/y to nextComponent coordinates
x -= nextComponent.getX();
y -= nextComponent.getY();
// check if we have a CompoundExpression at x/y
Component c = nextComponent.getComponentAt(x, y);
if (c != null)
{
// translate and dispatch the event for the CompoundExpression
MouseEvent ne = new MouseEvent(c, e.getID(), e.getWhen(), e.getModifiers(), x - c.getX(), y - c.getY(), e
.getClickCount(), e.isPopupTrigger(), e.getButton());
c.dispatchEvent(ne);
}
}
}
catch (ArrayIndexOutOfBoundsException exn)
{
// ignore, no child then
}
}
@Override
protected void processMouseMotionEvent(MouseEvent e)
{
// let this component handle the event first
super.processMouseMotionEvent(e);
try
{
// check if we have a next SmallStepProofNode
ProofNode node = this.proofNode.getChildAt(0);
// determine the SmallStepNodeComponent for the next proof node
SmallStepNodeComponent nextComponent = (SmallStepNodeComponent) node.getUserObject();
if (nextComponent != null)
{
// translate x/y to world coordinates
int x = e.getX() + getX();
int y = e.getY() + getY();
// translate x/y to nextComponent coordinates
x -= nextComponent.getX();
y -= nextComponent.getY();
// check if we have a CompoundExpression at x/y
Component c = nextComponent.getComponentAt(x, y);
if (c != null)
{
// translate and dispatch the event for the CompoundExpression
MouseEvent ne = new MouseEvent(c, e.getID(), e.getWhen(), e.getModifiers(), x - c.getX(), y - c.getY(), e
.getClickCount(), e.isPopupTrigger(), e.getButton());
c.dispatchEvent(ne);
}
}
}
catch (ArrayIndexOutOfBoundsException exn)
{
// ignore, no child then
}
}
// WORKAROUND: END
/**
* Causes the expression and the resultexpression to recalculate their layout.
*/
public void reset()
{
this.compoundExpression.reset();
}
/**
* Sets whether the small step view operates in advanced or beginner mode.
*
* @param advanced <code>true</code> to display only axiom rules in the
* menu.
* @see SmallStepComponent#setAdvanced(boolean)
*/
void setAdvanced(boolean advanced)
{
this.advanced = advanced ;
// Fill the menu with menuitems
ProofRule[] rulesFromModel = this.proofModel.getRules();
Language lang = this.proofModel.getLanguage();
this.menu = new JPopupMenu();
this.menu = this.rulesMenu.getMenu(rulesFromModel, rulesFromModel, lang, this, "smallstep", advanced);
// menu = new JPopupMenu();
// if to many rules we will devide in menu and submenus, otherwise there
// will be only seperators
// between the rules coming from the different languages
// if (rules.length > TOMANY)
// menu.addSeparator();
// menu.add(new MenuGuessItem());
// menu.add(new MenuGuessTreeItem());
this.menu.add(this.translateItem);
this.rules.getMenuButton().setMenu(this.menu);
}
/**
* Resets the expression of the {@link #currentUnderlineExpression}, if it
* has changed, and informs the {@link #compoundExpression}-Renderer that is
* has changed. Causes all other nodes within the tree to free theire
* underlining.
*
* @param expression
*/
void updateUnderlineExpression(Expression expression)
{
if (this.currentUnderlineExpression == expression)
{
return;
}
this.currentUnderlineExpression = expression;
this.compoundExpression.setUnderlineExpression(this.currentUnderlineExpression);
// free all the other nodes
freeUnderliningSibling(true, Direction.DIRECTION_CHILD);
freeUnderliningSibling(true, Direction.DIRECTION_PARENT);
}
/**
* Delegates the updating of the underline to
* {@link #updateUnderlineExpression(Expression)} with the expression stored
* within the label.
*
* @param label The label from the {@link SmallStepRulesComponent}.
*/
void updateUnderlineExpression(SmallStepRuleLabel label)
{
updateUnderlineExpression(label.getStepExpression());
}
/**
* Delegates the updating of the underline to
* {@link #updateUnderlineExpression(Expression)} with the expression of the
* first unproved {@link ProofStep}.
*
* @param button The button from the {@link SmallStepRulesComponent}.
*/
void updateUnderlineExpression(MenuButton button)
{
ProofStep[] steps = this.proofModel.remaining(this.proofNode);
if (steps.length == 0)
{
return;
}
if ( this.advanced)
{
updateUnderlineExpression(steps[steps.length-1].getExpression());
}
else
{
updateUnderlineExpression(steps[0].getExpression());
}
}
/**
* Called when an {@link JMenuItem} from the Menu was selected.<br>
* <br>
* No matter what item was selected the underlining of <b>all</b> nodes is
* cleared.<br>
* There are four possible actions to be done when an item is selected.<br>
* 1. a rule could have been selected, that should be used to proov this step.<br>
* 2. the model should guess the current node. 3. the model should complete
* the entire expression 4. the model should translate the current expression
* into core-syntax. This item mab be disable if there is no syntactical sugar
* within the expression.
*
* @param item
*/
public void handleMenuActivated(JMenuItem item)
{
freeUnderlining();
if (item instanceof MenuRuleItem)
{
MenuRuleItem ruleItem = (MenuRuleItem) item;
ProofRule rule = ruleItem.getRule();
try
{
this.proofModel.prove(rule, this.proofNode);
this.rules.setRightRule();
}
catch (Exception exc)
{
// revert the changes at the menu
this.rulesMenu.revertMenu();
this.rulesMenu.save();
this.rules.setWrongRule(rule);
}
fireNodeChanged();
}
else if (item instanceof MenuGuessItem)
{
try
{
this.proofModel.guess(this.proofNode);
}
catch (final ProofGuessException e)
{
fireRequstJumpToNode(e.getNode());
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
JOptionPane
.showMessageDialog(
getTopLevelAncestor(),
MessageFormat.format(Messages.getString("NodeComponent.5"), e.getMessage()), Messages.getString("NodeComponent.6"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
}
});
}
}
else if (item instanceof MenuGuessTreeItem)
{
try
{
this.proofModel.complete(this.proofNode);
}
catch (final ProofGuessException e)
{
fireRequstJumpToNode(e.getNode());
SwingUtilities.invokeLater(new Runnable() {
public void run()
{
JOptionPane
.showMessageDialog(
getTopLevelAncestor(),
MessageFormat.format(Messages.getString("NodeComponent.7"), e.getMessage()), Messages.getString("NodeComponent.8"), JOptionPane.ERROR_MESSAGE); //$NON-NLS-1$ //$NON-NLS-2$
}
});
}
}
else if (item instanceof MenuTranslateItem)
{
int answer = 1;
if (this.proofModel.containsSyntacticSugar(this.proofNode, false))
{
String[] answers =
{
Messages.getString("NodeComponent.0"), Messages.getString("NodeComponent.1"), Messages.getString("NodeComponent.2") }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
answer = JOptionPane.showOptionDialog(getTopLevelAncestor(), Messages.getString("NodeComponent.3"), //$NON-NLS-1$
Messages.getString("NodeComponent.4"), //$NON-NLS-1$
JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, answers, answers[0]);
}
switch (answer)
{
case 0:
this.proofModel.translateToCoreSyntax(this.proofNode, false);
break;
case 1:
this.proofModel.translateToCoreSyntax(this.proofNode, true);
break;
case 2:
break;
}
fireNodeChanged();
}
}
/**
* Sets the top-left position where the stuff should be appear
*
* @param pOrigin
*/
public void setOrigion(Point pOrigin)
{
this.origin = pOrigin;
}
/**
* Delegate the call to the {@link Component#setBounds(int, int, int, int)}
* method.<br>
* The position comes directly from the {@link #origin}. The size is a
* compined needed dimension of the {@link #ruleDimension} and the
* {@link #expressionDimension}.
*/
public void setBounds()
{
setBounds(this.origin.x, this.origin.y, this.ruleDimension.width + this.expressionDimension.width
+ this.spacing, this.actualRuleHeight + this.actualExpressionHeight + this.spacing);
}
/**
* Delegate the call to the {@link Component#setBounds(int, int, int, int)}
* method.<br>
* The position comes directly from the {@link #origin}. The size is a
* compined needed dimension of the {@link #ruleDimension} and the
* {@link #expressionDimension}.
*/
public void setBounds(int space)
{
setBounds(this.origin.x, this.origin.y, this.ruleDimension.width + this.expressionDimension.width
+ this.spacing, this.actualRuleHeight + this.actualExpressionHeight + this.spacing+space);
}
/**
* Causes an update of the {@link #compoundExpression} and the
* {@link #translateItem}.<br>
* Hands the store to the expression if memory is enabled.<br>
* Whether the expression of the {@link #proofNode} contains syntactical sugar
* the {@link #translateItem} is enabled or disabled at this point.
*/
public void update()
{
this.compoundExpression.setExpression(this.proofNode.getExpression());
if (this.memoryEnabled)
{
this.compoundExpression.setEnvironment(this.proofNode.getStore());
}
else
{
this.compoundExpression.setEnvironment(null);
}
this.translateItem.setEnabled(this.translator.containsSyntacticSugar(this.proofNode.getExpression(), true));
}
//
// Stuff for the rules
//
/**
* Returns the minmal size the rules need to render themself.<br>
* The {@link #actualRuleHeight} is initiated at this point with the minimum
* height.
*
* @return The minimum size the rules need to render themself.
*/
public Dimension getMinRuleSize()
{
this.ruleDimension = this.rules.getNeededSize(this.underlineRuleAdapter);
this.actualRuleHeight = this.ruleDimension.height;
return this.ruleDimension;
}
/**
* Returns the current {@link #ruleDimension}.<br>
* This is not necessarily the minimum Size. It may be altered by
* {@link #setActualRuleHeight(int)} and {@link #setMaxRuleWidth(int)}.
*
* @return Returns the currently set size of the rules.
*/
public Dimension getRuleSize()
{
return this.ruleDimension;
}
/**
* Sets the width the {@link #rules} have to use.<br>
* This is needed because all rules, no matter how big they are, need to use
* all the same size.
*
* @param maxRuleWidth The width of the biggest rule within the tree.
*/
public void setMaxRuleWidth(int maxRuleWidth)
{
this.rules.setActualWidth(maxRuleWidth);
this.ruleDimension.width = maxRuleWidth;
}
/**
* Sets the actual height the rule should use.<br>
* This is needed because right of this {@link #rules} item the expression of
* the child nodes is located and they needed to be aligned.
*
* @param pActualRuleHeight
*/
public void setActualRuleHeight(int pActualRuleHeight)
{
this.actualRuleHeight = pActualRuleHeight;
}
/**
* Returns the actual rule height.
*
* @return The actual rule height.
*/
public int getActualRuleHeight()
{
return this.actualRuleHeight;
}
/**
* Returns the top position of the {@link #rules}.<br>
* That actualy is the bottom position of the {@link #compoundExpression}
* added with some {@link #spacing}.
*
* @return
*/
public int getRuleTop()
{
return this.actualExpressionHeight + this.spacing;
}
/**
* Hides the rules.<br>
* This is done if the this node is the last node witin the tree. So no
* further rules could be applied.
*/
public void hideRules()
{
this.rules.setVisible(false);
}
/**
* Unhides the rules.
*/
public void showRules()
{
this.rules.setVisible(true);
}
/**
* Causes the rules to get places.<br>
* The top position of the rules is chosen that the {@link #rules}-item is
* verticaly centered in the space available.
*/
public void placeRules()
{
int top = getRuleTop() + (this.actualRuleHeight - this.ruleDimension.height) / 2;
this.rules.setBounds(0, top, this.ruleDimension.width, this.ruleDimension.height);
}
/**
* Causes the rules to get places moved by y<br>
*
* The top position of the rules is chosen that the {@link #rules}-item is
* verticaly centered in the space available.
* @param y the amount to move
*/
public void placeRules(int y)
{
int top = getRuleTop() + (this.actualRuleHeight - this.ruleDimension.height) / 2 +y;
this.rules.setBounds(0, top, this.ruleDimension.width, this.ruleDimension.height);
}
/**
* Causes every node, including this one, to get freed from the underlining.
*/
private void freeUnderlining()
{
freeUnderliningSibling(false, Direction.DIRECTION_CHILD);
freeUnderliningSibling(false, Direction.DIRECTION_PARENT);
}
/**
* Frees every node in the given direction to be freed from the underlining.
* If <i>ignoreThis</i> is <i>false</i> the current nodes is freed aswell.
*
* @param ignoreThis Whether the current node should not be freed aswell.
* @param direction The direction how the freeing should be done.
*/
private void freeUnderliningSibling(boolean ignoreThis, Direction direction)
{
if (!ignoreThis)
{
this.compoundExpression.setUnderlineExpression(null);
}
SmallStepProofNode nextNode = null;
switch (direction)
{
case DIRECTION_CHILD:
try
{
nextNode = this.proofNode.getFirstChild();
}
catch (Exception e)
{
// nothing to do
}
break;
case DIRECTION_PARENT:
nextNode = this.proofNode.getParent();
break;
}
if (nextNode == null)
{
// no next node, so we're done here
return;
}
SmallStepNodeComponent nextNodeComponent = (SmallStepNodeComponent) nextNode.getUserObject();
nextNodeComponent.freeUnderliningSibling(false, direction);
}
//
// Stuff for the expressions
//
/**
* Returns the minimum size needed to correctly render the {@link expression}.
* The {@link #actualExpressionHeight} is initiated with the heigth of the
* minimum size.
*
* @param pMaxWidth Max width is given for the entire component.
* @return
*/
public Dimension checkNeededExpressionSize(int pMaxWidth)
{
int maxWidth = pMaxWidth;
maxWidth -= this.ruleDimension.width + this.spacing;
this.expressionDimension = this.compoundExpression.getNeededSize(maxWidth);
// use the calculated expression height for the actual height
// until it will be changed by the SmallStepComponent
this.actualExpressionHeight = this.expressionDimension.height;
return this.expressionDimension;
}
/**
* Returns the size of the expression.
*
* @return
*/
public Dimension getExpressionSize()
{
return this.expressionDimension;
}
/**
* Sets the actual expression height.
*
* @param pActualExpressionHeight
*/
public void setActualExpressionHeight(int pActualExpressionHeight)
{
this.actualExpressionHeight = pActualExpressionHeight;
}
/**
* Returns the actual height of the expression.
*
* @return
*/
public int getActualExpressionHeight()
{
return this.actualExpressionHeight;
}
/**
* Causes the {@link #compoundExpression} to get placed.<br>
* The left position of the expression is actualy the width of the rules added
* with some {@link #spacing}. If the expression is lower than the rulelabels the
* expression will be centerd.
*/
public void placeExpression()
{
if (this.expressionDimension.height < this.actualExpressionHeight)
{
int diff = this.actualExpressionHeight - this.expressionDimension.height;
this.compoundExpression.setBounds(this.ruleDimension.width + this.spacing, diff / 2,
this.expressionDimension.width, this.expressionDimension.height);
}
else
{
this.compoundExpression.setBounds(this.ruleDimension.width + this.spacing, 0, this.expressionDimension.width,
this.actualExpressionHeight);
}
}
/**
* adds a {@link SmallStepNodeListener} to the listenerList of the component
*
* @param listener the SmallStepNodeListener to add
*/
public void addSmallStepNodeListener(SmallStepNodeListener listener)
{
this.listenerList.add(SmallStepNodeListener.class, listener);
}
/**
* removes a {@link SmallStepNodeListener} from the listenerList of the component
*
* @param listener
*/
public void removeSmallStepNodeListener(SmallStepNodeListener listener)
{
this.listenerList.remove(SmallStepNodeListener.class, listener);
}
/***/
private void fireNodeChanged()
{
Object[] listeners = this.listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2)
{
if (listeners[i] != SmallStepNodeListener.class)
{
continue;
}
((SmallStepNodeListener) listeners[i + 1]).nodeChanged(this);
}
}
/***/
private void fireRequstJumpToNode(ProofNode node)
{
Object[] listeners = this.listenerList.getListenerList();
for (int i = 0; i < listeners.length; i += 2)
{
if (listeners[i] != SmallStepNodeListener.class)
{
continue;
}
((SmallStepNodeListener) listeners[i + 1]).requestJumpToNode(node);
}
}
/**
* Returns the compoundExpression.
*
* @return The compoundExpression.
* @see #compoundExpression
*/
public CompoundExpression<Location, Expression> getCompoundExpression()
{
return this.compoundExpression;
}
}