Cet aperçu des bibliothèques Logisim se termine par un compteur Gray assez sophistiqué qui permet à l'utilisateur de modifier sa valeur actuelle à l'aide de l'outil Poke et de placer une étiquette sur le composant à l'aide de l'outil Text. Il personnalise également l'icône qui apparaît dans l'explorateur, associée à l'outil.
package com.cburch.gray;
import java.net.URL;
import javax.swing.ImageIcon;
import com.cburch.logisim.data.Attribute;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Direction;
import com.cburch.logisim.instance.Instance;
import com.cburch.logisim.instance.InstanceFactory;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.Port;
import com.cburch.logisim.instance.StdAttr;
import com.cburch.logisim.util.GraphicsUtil;
import com.cburch.logisim.util.StringUtil;
/** Fabrique un compteur qui itère sur les codes Gray. Ce compteur présente
* plusieurs caractéristiques supplémentaires par rapport à la classe
* SimpleGrayCounter. /*
class GrayCounter extends InstanceFactory {
public GrayCounter() {
super("Gray Counter");
setOffsetBounds(Bounds.create(-30, -15, 30, 30));
setPorts(new Port[] {
new Port(-30, 0, Port.INPUT, 1),
new Port( 0, 0, Port.OUTPUT, StdAttr.WIDTH),
});
/** Nous aurons des attributs de largeur, d'étiquette et de police
* d'étiquette. Ces deux derniers attributs nous permettent d'associer
* une étiquette au composant (bien que nous ayons également besoin de
* configureNewInstance pour configurer l'emplacement de l'étiquette).*/
setAttributes(
new Attribute[] { StdAttr.WIDTH, StdAttr.LABEL, StdAttr.LABEL_FONT },
new Object[] { BitWidth.create(4), "", StdAttr.DEFAULT_LABEL_FONT });
/** L'invocation de la méthode suivante permet de manipuler l'état
* de l'instance à l'aide de l'outil Poke. */
setInstancePoker(CounterPoker.class);
/** Les deux lignes suivantes font en sorte que la fenêtre de l'explorateur
* affiche une icône personnalisée représentant le type de composant.
* Il s'agit d'une image de 16x16. */
URL url = getClass().getClassLoader().getResource("com/cburch/gray/counter.gif");
if(url != null) setIcon(new ImageIcon(url));
}
/** La méthode configureNewInstance est invoquée à chaque fois qu'une nouvelle
* instance est créée. Dans la superclasse, la méthode ne fait rien, puisque
* la nouvelle instance est déjà bien configurée par défaut. Mais il arrive
* que l'on doive faire quelque chose de particulier pour chaque instance,
* c'est pourquoi il faut surcharger la méthode. Dans ce cas, nous devons
* configurer l'emplacement de son étiquette. */
protected void configureNewInstance(Instance instance) {
Bounds bds = instance.getBounds();
instance.setTextField(StdAttr.LABEL, StdAttr.LABEL_FONT,
bds.getX() + bds.getWidth() / 2, bds.getY() - 3,
GraphicsUtil.H_CENTER, GraphicsUtil.V_BASELINE);
}
public void propagate(InstanceState state) {
/** C'est la même chose que pour SimpleGrayCounter, sauf que nous utilisons
* l'attribut StdAttr.WIDTH pour déterminer la largeur de bit avec laquelle
* nous devons travailler. */
BitWidth width = state.getAttributeValue(StdAttr.WIDTH);
CounterData cur = CounterData.get(state, width);
boolean trigger = cur.updateClock(state.getPort(0));
if(trigger) cur.setValue(GrayIncrementer.nextGray(cur.getValue()));
state.setPort(1, cur.getValue(), 9);
}
public void paintInstance(InstancePainter painter) {
/** Il s'agit essentiellement de la même chose que pour SimpleGrayCounter,
* à l'exception de l'invocation de painter.drawLabel pour dessiner
* l'étiquette. */
painter.drawBounds();
painter.drawClock(0, Direction.EAST);
painter.drawPort(1);
painter.drawLabel();
if(painter.getShowState()) {
BitWidth width = painter.getAttributeValue(StdAttr.WIDTH);
CounterData state = CounterData.get(painter, width);
Bounds bds = painter.getBounds();
GraphicsUtil.drawCenteredText(painter.getGraphics(),
StringUtil.toHexString(width.getWidth(), state.getValue().toIntValue()),
bds.getX() + bds.getWidth() / 2,
bds.getY() + bds.getHeight() / 2);
}
}
}
package com.cburch.gray;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import com.cburch.logisim.data.BitWidth;
import com.cburch.logisim.data.Bounds;
import com.cburch.logisim.data.Value;
import com.cburch.logisim.instance.InstancePainter;
import com.cburch.logisim.instance.InstancePoker;
import com.cburch.logisim.instance.InstanceState;
import com.cburch.logisim.instance.StdAttr;
/** Lorsque l'utilisateur clique sur un compteur à l'aide de l'outil Poke,
* un objet CounterPoker est créé et cet objet gère tous les événements de
* l'utilisateur. Notez que CounterPoker est une classe spécifique à
* GrayCounter, et qu'elle doit être une sous-classe de InstancePoker dans
* le paquetage com.cburch.logisim.instance. */
public class CounterPoker extends InstancePoker {
public CounterPoker() {}
/** Détermine si l'endroit où la souris a été pressée doit entraîner
* le déclenchement d'un poke. */
public boolean init(InstanceState state, MouseEvent e) {
return state.getInstance().getBounds().contains(e.getX(), e.getY());
// N'importe quel endroit du rectangle principal déclenche le poke. L'utilisateur peut
//avoir cliqué à l'intérieur d'une étiquette, mais celle-ci sera en dehors des limites.
}
/** Dessine un indicateur indiquant que le curseur est sélectionné. Ici,
* nous allons dessiner un rectangle rouge autour de la valeur. */
public void paint(InstancePainter painter) {
Bounds bds = painter.getBounds();
BitWidth width = painter.getAttributeValue(StdAttr.WIDTH);
int len = (width.getWidth() + 3) / 4;
Graphics g = painter.getGraphics();
g.setColor(Color.RED);
int wid = 7 * len + 2; // width of caret rectangle
int ht = 16; // height of caret rectangle
g.drawRect(bds.getX() + (bds.getWidth() - wid) / 2,
bds.getY() + (bds.getHeight() - ht) / 2, wid, ht);
g.setColor(Color.BLACK);
}
/** Traite une touche en l'ajoutant simplement à la fin de la valeur actuelle. */
public void keyTyped(InstanceState state, KeyEvent e) {
/* le convertit en chiffre hexadécimal ; s'il ne s'agit pas
d'un chiffre hexadécimal, l'opération est interrompue.*/
int val = Character.digit(e.getKeyChar(), 16);
BitWidth width = state.getAttributeValue(StdAttr.WIDTH);
if(val < 0 || (val & width.getMask()) != val) return;
/* calculer la valeur suivante*/
CounterData cur = CounterData.get(state, width);
int newVal = (cur.getValue().toIntValue() * 16 + val) & width.getMask();
Value newValue = Value.createKnown(width, newVal);
cur.setValue(newValue);
state.fireInvalidated();
/** Vous pourriez être tenté de propager la valeur immédiatement ici,
* en utilisant state.setPort. Cependant, le circuit peut actuellement
* se propager dans un autre thread, et l'appel direct de setPort
* pourrait interférer avec cela. L'utilisation de fireInvalidated
* notifie le thread de propagation à Invoquer Propager sur le
* compteur à sa prochaine occasion.
}
}
Suite : Lignes directrices.