package de.unisiegen.tpml.graphics.smallstep; import java.awt.Color; import java.awt.Dimension; import java.awt.Graphics; import java.awt.event.MouseMotionAdapter; import java.util.LinkedList; import javax.swing.JComponent; import de.unisiegen.tpml.core.ProofRule; import de.unisiegen.tpml.core.ProofStep; import de.unisiegen.tpml.core.smallstep.SmallStepProofNode; import de.unisiegen.tpml.core.smallstep.SmallStepProofRule; import de.unisiegen.tpml.graphics.components.MenuButton; /** * Component containing the RuleItems.<br> * <br> * The Rule items of a <i>SmallStepRulesComponnent</i> are * {@link SmallStepRuleLabel}s showing the rules * already applied to the node, a {@link MenuButton} * showing a rule that has not yet been evaluated and * the arrow separating the <i>MetaRules</i> from the * <i>AxiomRule</i>.<br> * <br> * The following image demonstrates a usual appearance of * a <code>SmallStepRulesComponent</code> with a few <i>MetaRules</i> * (two of the them are grouped together) * and the applied <i>AxiomRule</i>:<br> * <br> * <img src="../../../../../../images/rulescomponent.png" /><br> * <br> * In the following image you just can see the boundings of all * rule labels.<br> * <img src="../../../../../../images/rulescomponent_scheme.png" /><br> * <br> * There is no <code>spacing/code> between the labels. But there is the * an amount of {@link #spacing} between the centering line * (the one of the arrow) and the bottom of the <i>MetaRules</i> * and the top of the <i>AxiomRule</i>.<br> * <br> * The imported information about the layout on this * is shown in {@link #getNeededSize(MouseMotionAdapter)}. The calling * of this method not only returns the needed space for this component * it also arranges the placing of all labels within it.<br> * <br> * <b><code>IMPORTANT:</code></b> When assigned space for this component by calling * {@link java.awt.Component#setBounds(int, int, int, int)} the vertical alignment * must be done manualy. <b>This component is always top-aligned</br>. * <br> * * * @author marcell * @see de.unisiegen.tpml.graphics.smallstep.SmallStepView * @see de.unisiegen.tpml.graphics.smallstep.SmallStepComponent * @see de.unisiegen.tpml.graphics.smallstep.SmallStepNodeComponent * @see de.unisiegen.tpml.graphics.smallstep.SmallStepRuleLabel * */ public class SmallStepRulesComponent extends JComponent { /** * */ private static final long serialVersionUID = -6291688672308787914L; /** * */ private SmallStepProofNode proofNode; /** * The list of {@link SmallStepRuleLabel}s containing the * information of all rules that have been evaluated. */ private LinkedList<SmallStepRuleLabel> ruleLabels; /** * The {@link MenuButton} for the Rule(s) that have * not yet been evaluated. */ private MenuButton menuButton; /** * The spacing in pixels between the labels/menuButton * itself and between the labales/menuButton and the * arrow. */ private int spacing; /** * The size in pixels for the arrowhead. */ private int arrowSize; /** * After the layouting of the {@link SmallStepComponent} the size * contains the actual size of this component. */ private Dimension size; /** * Create a new {@link SmallStepRulesComponent} for the * given proofNode.<br> * <br> * The {@link #menuButton} is already created but not yet shown. * * @param proofNode The {@link SmallStepProofNode} containing the * origin informations. */ public SmallStepRulesComponent (SmallStepProofNode proofNode) { this.proofNode = proofNode; this.ruleLabels = new LinkedList<SmallStepRuleLabel> (); this.menuButton = new MenuButton (); add (this.menuButton); this.menuButton.setVisible (true); this.spacing = 6; this.arrowSize = 6; } /** * Returns the {@link #menuButton} * * @return The menuButton. */ public MenuButton getMenuButton () { return this.menuButton; } /** * Causes the name of the given rule be set for the text * of the {@link #menuButton} and the test will be shown in red. * * @param rule The wrong rule. */ public void setWrongRule (ProofRule rule) { this.menuButton.setText("(" + rule.getName () + ")"); this.menuButton.setTextColor(Color.RED); } /** * Resets the name and the color (Black) of the {@link #menuButton}. * */ public void setRightRule () { this.menuButton.setText(""); this.menuButton.setTextColor(Color.BLACK); } /** * Sets the actual width for the component.<br> * <br> * The actual width comes directly from the {@link SmallStepComponent} * and is the maximum width of all rule components within the entire * proofTree. * * @param width */ public void setActualWidth (int width) { Dimension size = new Dimension (width, this.size.height); setSize (size); setPreferredSize (size); setMinimumSize (size); setMaximumSize (size); } /** * Places all {@link #ruleLabels} and the {@link #menuButton} * and returns the minimum needed size.<br> * <br> * First of this function all currently added labels will be removed * and theire mouse motion adapter removed aswell. So then we have a clean * component. * <br> * This functions iterates through all steps and just places them. * The <i>MetaRule</i>s will be placed on top of the arrow-beam and the * <i>AxiomRule</i> will be placed below.<br> * If the currently evaluated steps don't end with an <i>AxiomRule</i> * the {@link #menuButton} will be placed behind the last <i>MetaRule</i> * above the arrow-beam. When it ends with an <i>AxiomRule</i> it will be * placed at the beginning of the row below the arrow-beam.<br> * If there is more than one Rule of a kind directly folowing another, those * Rules will be grouped together. The number of rules beeing grouped together * is shown in the exponent. See {@link SmallStepRuleLabel} for this.<br> * <br> * Every item (except the grouped items) in this component gets added * the given adapter to determin in the {@link SmallStepComponent} what * rule is below the mouse, so the corresponding expression could be * underlined. * * @param adapter * * @return */ public Dimension getNeededSize (MouseMotionAdapter adapter) { int labelHeight = Math.max (SmallStepRuleLabel.getLabelHeight(), this.menuButton.getHeight()); int neededHeight = 2 * labelHeight + 2 * this.spacing + this.arrowSize; int centerV = neededHeight / 2; this.size = new Dimension (0, neededHeight); // clear all the labels that are currently for (SmallStepRuleLabel l : this.ruleLabels) { l.removeMouseMotionListener(adapter); remove (l); } this.ruleLabels.clear(); ProofStep[] steps = this.proofNode.getSteps(); if (steps.length > 0) { // first reference rule will be the first node ProofStep step = steps [0]; int count = 1; for (int i=1; i<steps.length; i++) { ProofStep cStep = steps [i]; // when the next rule is of the same type just increment the // counter and wait until a different rule comes if (cStep.getRule().equals (step.getRule())) { ++count; } else { SmallStepRuleLabel label = new SmallStepRuleLabel (step.getRule ().getName (), count); label.addMouseMotionListener(adapter); if (count == 1) { label.setStepExpression(step.getExpression()); } // add the label to the gui and to the list of all labels add (label); this.ruleLabels.add(label); Dimension labelSize = label.getPreferredSize(); // put the label with a bit spacing on top of the centering line label.setBounds(this.size.width, centerV - labelSize.height - this.spacing, labelSize.width, labelSize.height); this.size.width += labelSize.width; // the actual node this the new reference node step = cStep; count = 1; } } if (((SmallStepProofRule)step.getRule()).isAxiom()) { SmallStepRuleLabel label = new SmallStepRuleLabel (step.getRule ().getName (), count); label.addMouseMotionListener(adapter); if (count == 1) { label.setStepExpression(step.getExpression()); } // add the label to the gui and to the list of all labels add (label); this.ruleLabels.add(label); Dimension labelSize = label.getPreferredSize(); // put the label with a bit spacing at the bottom of the centering line label.setBounds(0, centerV + this.spacing, labelSize.width, labelSize.height); this.size.width = Math.max (this.size.width, labelSize.width); // we are through with this rulepack so its ot needed to display the menuButton this.menuButton.setVisible(false); } else { SmallStepRuleLabel label = new SmallStepRuleLabel (step.getRule ().getName (), count); label.addMouseMotionListener(adapter); if (count == 1) { label.setStepExpression(step.getExpression()); } // add the label to the gui and to the list of all labels add (label); this.ruleLabels.add(label); Dimension labelSize = label.getPreferredSize(); label.setBounds(this.size.width, centerV - labelSize.height - this.spacing, labelSize.width, labelSize.height); this.size.width += labelSize.width; // this rulepack doesn't end with an axiom rule so we need to display the menuButton Dimension buttonSize = this.menuButton.getNeededSize(); this.menuButton.setBounds(this.size.width, centerV - this.spacing - buttonSize.height, buttonSize.width, buttonSize.height); this.menuButton.setVisible (true); this.size.width += buttonSize.width; } } else { // no rule at all evaluated Dimension buttonSize = this.menuButton.getNeededSize(); this.menuButton.setBounds(this.size.width, centerV - this.spacing - buttonSize.height, buttonSize.width, buttonSize.height); this.menuButton.setVisible (true); this.size.width += buttonSize.width; } this.size.width += this.arrowSize*2; return this.size; } /** * Just paints the arrow; all other components draw themself. */ @Override protected void paintComponent (Graphics gc) { int centerV = getHeight () / 2; gc.setColor(Color.BLACK); gc.drawLine (0, centerV, getWidth () - 1, centerV); gc.drawLine (getWidth () - 1, centerV, getWidth () - 1 - this.arrowSize, centerV - this.arrowSize); gc.drawLine (getWidth () - 1, centerV, getWidth () - 1 - this.arrowSize, centerV + this.arrowSize); } }