Here is the code in PingPongActionThread.
private Vector listeners; // List of StringShower listeners
...
public void addStringShower(StringShower ss) {
listeners.addElement(ss);
}
public void run() {
try {
for(int i=0;i < count-1;i++) {
showAll(id,i+": "+what);
sleep(delay); // wait until next time
checkSuspend();
}
} catch (InterruptedException e) {}
showAll(id,(count-1)+": "+what+"\n"+what+" done");
}
private void showAll(int id, String str) {
StringShower ss;
for (int i=0;i < listeners.size();i++) {
ss = (StringShower)listeners.elementAt(i);
ss.showString(id,str);
}
}
TextAreaStringShower outputCommon;
TextAreaStringShower outputPing;
TextAreaStringShower outputPong;
...
outputCommon = new TextAreaStringShower(18,18);
outputPing = new TextAreaStringShower(18,18);
outputPong = new TextAreaStringShower(18,18);
...
private void createPing() {
ping = new PingPongActionThread(PINGID,"ping", 2000, 10);
ping.addStringShower(this);
ping.addStringShower(outputPing);
ping.addStringShower(outputCommon);
}
...
void suspendThreads() {
outputPing.showString(-1,"ping suspended");
outputPong.showString(-1,"PONG suspended");
ping.setSuspend(true);
pong.setSuspend(true);
}
...
etc.
Here is what the TextAreaStringShower looks like:
import java.awt.*;
public class TextAreaStringShower extends TextArea
implements StringShower {
public TextAreaStringShower(int n, int m) {
super(n,m);
}
public void showString(int id, String str) {
append(str+"\n");
}
}
Here is the StringCanvas class
import java.awt.*;
public class StringCanvas extends Canvas {
private Image buffer;
private int currentWidth = 0;
private int currentHeight = 0;
private int imageWidth;
private int imageHeight;
private Graphics gc;
private Color foreColor = Color.black;
private Color backColor = Color.orange;
public void drawStrings(String[] strs) {
setupImages();
if (buffer == null) return;
gc.setColor(backColor);
gc.fillRect(0,0,imageWidth,imageHeight);
gc.setColor(foreColor);
if (strs == null) {
repaint();
return;
}
for (int i=0;i < strs.length;i++)
gc.drawString(strs[i],10,15*(i+1));
repaint();
}
private void setupImages() {
if ( (buffer != null) && (getBounds().width == currentWidth) &&
(getBounds().height == currentHeight) ) return;
imageWidth = getBounds().width;
imageHeight = getBounds().height;
if ( (imageWidth <= 0) || (imageHeight <= 0) ) return;
buffer = createImage(imageWidth,imageHeight);
currentWidth = getBounds().width;
currentHeight = getBounds().height;
gc = buffer.getGraphics();
gc.setColor(Color.black);
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
setupImages();
if (buffer == null) return;
g.drawImage(buffer,0,0,null);
}
}
The applet keeps track of the ping and pong counts. It ignores the String
passed to it in showString and just uses the id to determine
whether it was called by ping or pong.
Here is the applet's showString method:
public void showString(int id, String str) {
if (id == PINGID) {
pingCount++;
pingClip.play();
}
else if (id == PONGID) {
pongCount++;
pongClip.play();
}
repaint();
}
Here is the applet's paint method:
public void paint(Graphics g) {
strs[0] = "ping count = "+pingCount;
strs[1] = "pong count = "+pongCount;
if (useActive)
strs[2] = "thread count"+getActiveCount();
else
strs[2] = "";
mc.drawStrings(strs);
}
Here is the showString method of the StringCanvas class:
public void showString(int id, String str) {
if (id == PingPongActionThread.PINGID)
pingCount++;
else if (id == PingPongActionThread.PONGID)
pongCount++;
fillBuffer();
repaint();
}
And here is the paint method:
public void paint(Graphics g) {
fillBuffer();
if (buffer == null) return;
g.drawImage(buffer,0,0,null);
}
All of the work is in the fillBuffer method:
private synchronized void fillBuffer() {
setupImages();
if (buffer == null) return;
gc.setColor(backColor);
gc.fillRect(0,0,imageWidth,imageHeight);
gc.setColor(foreColor);
gc.drawString("ping count = "+pingCount,10,15);
gc.drawString("pong count = "+pongCount,10,30);
if (useActive)
gc.drawString("thread count: "+Thread.activeCount(),10,45);
}
Since more than one thread can call showString, we make
fileBuffer a synchronized method.Why does this need to be synchronized?
The applet still implements StringShower so that it can play the audio clips and start and stop the threads when necessary.
When the threads are created, the StringShowers need to be set up and this now has to be done by the frame, so we add methods to PintPongActionFrame to do this:
public void setupPingListeners(PingPongActionThread ppt) {
ppt.addStringShower(outputCommon);
ppt.addStringShower(outputPing);
ppt.addStringShower(mc);
}
public void setupPongListeners(PingPongActionThread ppt) {
ppt.addStringShower(outputCommon);
ppt.addStringShower(outputPong);
ppt.addStringShower(mc);
}
These are used in the applet as follows:
private void createPing() {
ping =
new PingPongActionThread(PingPongActionThread.PINGID,"ping", 2000, 10);
ping.addStringShower(this);
ppf.setupPingListeners(ping);
}
private void createPong() {
pong =
new PingPongActionThread(PingPongActionThread.PONGID,"PONG", 3000, 5);
pong.addStringShower(this);
ppf.setupPongListeners(pong);
}
Here is the entire frame code:
import java.awt.*;
public class PingPongActionFrame extends Frame implements StringShower {
TextAreaStringShower outputCommon;
TextAreaStringShower outputPing;
TextAreaStringShower outputPong;
Button startButton;
PingPongActionThread ping;
PingPongActionThread pong;
int pingCount = 0;
int pongCount = 0;
Color backColor = Color.lightGray;
Color foreColor = Color.black;
StringCanvas mc;
boolean useActive = true;
String[] strs;
public PingPongActionFrame(boolean useAcitve, int w, int h) {
super("Step 6 written by S. Robbins");
this.useActive = useActive;
setupLayout(w,h);
setVisible(true);
}
private void setupLayout(int w, int h) {
setBackground(backColor);
setForeground(foreColor);
Panel outputPanel = new Panel();
setLayout(new BorderLayout());
outputPanel.setLayout(new GridLayout(1,3));
outputCommon = new TextAreaStringShower(18,18);
outputPing = new TextAreaStringShower(18,18);
outputPong = new TextAreaStringShower(18,18);
outputCommon.setBackground(Color.yellow);
outputPing.setBackground(Color.cyan);
outputPong.setBackground(Color.cyan);
outputCommon.setForeground(foreColor);
outputPing.setForeground(foreColor);
outputPong.setForeground(foreColor);
outputPanel.add(outputPing);
outputPanel.add(outputCommon);
outputPanel.add(outputPong);
add(BorderLayout.NORTH,outputPanel);
mc = new StringCanvas(useActive);
add(BorderLayout.CENTER,mc);
setSize(w,h);
}
public void setupPingListeners(PingPongActionThread ppt) {
ppt.addStringShower(outputCommon);
ppt.addStringShower(outputPing);
ppt.addStringShower(mc);
}
public void setupPongListeners(PingPongActionThread ppt) {
ppt.addStringShower(outputCommon);
ppt.addStringShower(outputPong);
ppt.addStringShower(mc);
}
public void paint(Graphics g) {
mc.repaint();
}
public void showString(int id, String str) {
if (id == PingPongActionThread.PINGID)
outputPing.showString(id,str);
else if (id == PingPongActionThread.PONGID)
outputPong.showString(id,str);
outputCommon.showString(id,str);
}
}
CSC Cards for Step 6:
None of the classes from the standard Java distribution are included.
Interface: StringShower Responsibilities: showString Collaborations: none Class: TextAreaStringShower Responsibilities: showString Collaborations: none Class: StringCanvas Responsibilities: showString update paint Collaborations: PingPongActionThread StringShower Class: PingPongActionThread Responsibilities: addStringShower run setSuspend Collaborations: StringShower Class: PingPongActionFrame Responsibilities: setupPingListeners setupPongListeners paint showString Collaborations: StringShower TextAreaStringShower PingPongActionThread StringCanvas Class: PingPongActionApplet Responsibilities: init start stop showString actionPerformed Collaborations: StringShower PingPongActionThread PingPongActionFrame
We can fix this by having StringCanvas display a line at a position in the canvas based on the id from the showString.
Here is what we can do to the StringCanvas
private Vector strs;
...
public StringCanvas() {
super();
strs = new Vector();
repaint();
}
public void showString(int id, String str) {
for (int i=strs.size();i < =id;i++)
strs.addElement("");
strs.setElementAt(str,id);
fillBuffer();
repaint();
}
private synchronized void fillBuffer() {
setupImages();
if (buffer == null) return;
gc.setColor(backColor);
gc.fillRect(0,0,imageWidth,imageHeight);
gc.setColor(foreColor);
for (int i=0;i < strs.size();i++)
gc.drawString((String)strs.elementAt(i),10,15*(i+1));
}
Note the initiali for loop in showString which makes sure we are not
trying to put something past the end of the vector.
This implementation assumes that the ping and pong ids are set to the line numbers we want them to display at.