「Java謎+落とし穴徹底解明」X-Drawのソースに関するバグ
このページは、拙書「Java謎+落とし穴徹底解明」の p.213からのX-Drawソースに関する正誤表です。
原因を記述しやすいよう、正誤表とは別ページとしました。 このページからダウンロードできるソースでは、バグはFIXされています。
発見の日付順に並んでいます(新しい方が下)。
Circle, Rectangleを選択してから、右ボタンでドラッグすると、 ラバーバンドだけが表示されます。
訂正箇所:DrawWindow.javaのmouseReleased()メソッド
誤
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) { button = DrawWindowListener.BUTTON1; } else if ((e.getModifiers() & InputEvent.BUTTON2_MASK) != 0) { button = DrawWindowListener.BUTTON2; } else {
正
if ((e.getModifiers() & InputEvent.BUTTON1_MASK) != 0) { button = DrawWindowListener.BUTTON1; } else if ((e.getModifiers() & InputEvent.BUTTON3_MASK) != 0) { button = DrawWindowListener.BUTTON2; } else {
UNIXと3ボタンマウスの環境なら、 BUTTON2_MASKが中央ボタンとして解釈されますが、 Windowsの場合右ボタンはBUTTON3_MASKになります。
最初の開発をLinux上で行ない、 あとでWindowsに持っていったときにここだけ直し忘れました。
マウスカーソルを動かしながらボタンを押したり離したりした時、 環境によって、最後の座標(ボタンを押したり離したりした座標)に対して MouseMotionListenerが呼び出されないものがあるようです。
書籍掲載版のX-Drawでは、最後の座標までイベントが来ることを前提として、 最後のラバーバンドの消去を行なわなかった(どうせ確定した図形に 上書きされると考えた)ため、 すばやく図形を描画すると最後のラバーバンドが残ります。
直すには、図形確定の際に最後のラバーバンドを消すようにします。
訂正箇所:
PolylineCommand.javaのmouseButtonPressed()メソッド
} else if (this.currentMode == DRAWING_MODE) { drawRubberBand(d, this.prevX, this.prevY); // 1行追加 d.drawLine(this.points[this.points.length-1].getX(),
CircleCommand.javaのmouseButtonReleased()メソッド
if (this.currentMode != DRAGGING_MODE) { this.currentMode = INITIAL_MODE; return; } d.setXORMode(true); // 3行追加 d.drawCircle(this.centerX, this.centerY, this.prevRadius); d.setXORMode(false); double radius = calcDistance(this.centerX, this.centerY, x, y);
RectangleCommand.javaのmouseButtonReleased()メソッド
if (this.currentMode != DRAGGING_MODE) { this.currentMode = INITIAL_MODE; return; } d.setXORMode(true); // 3行追加 d.drawRectangle(this.startX, this.startY, this.prevX, this.prevY); d.setXORMode(false); d.drawRectangle(this.startX, this.startY, x, y);
p.213 の最初が
iimport java.applet.Applet;
となっていますが、これはもちろん
import java.applet.Applet;
の間違いです。
12/14のバグと原因は同じですが、修正漏れがありました。
ポリラインの途中の点を指示した直後にカーソルが動くとその点に対して MouseMotionListenerが呼び出されないため、
訂正箇所:
PolylineCommand.javaのmouseButtonPressed()メソッド末尾
if (button == DrawWindowListener.BUTTON2) { shapeCollection.addShape(new Polyline(this.points)); this.currentMode = INITIAL_MODE; } else { this.currentMode = DRAWING_START_MODE; }
おかげさまで「Java謎+落とし穴徹底解明」の増刷(第2刷)が決まりましたので、 いくつか気になったところを修正しておきます。
XDrawApplet.javaの80行目付近:
private class XDrawWindowAdapter extends WindowAdapter {
他の箇所に合わせて、privateを付けておきます。
DrawWindow.java冒頭:
class DrawWindow implements Drawable, MouseListener, MouseMotionListener { // 座標変換←削除 private double xMin;
どうも意味不明のコメントなので、削ってしまいました。 確かに、この周辺のフィールドは座標変換に使われてはいるのですが...
掲示板の方で、内部クラスからその外側のクラスのフィールドを参照する時、 一箇所だけ「XDrawApplet.this.shapeCollection」 という書き方を使っているところがある、とのご指摘をいただきましたが、 これはそのままにしておきます。 p.295で説明している書き方の例ということで。
デバイス座標系とワールド座標系の変換部分にバグがありました。
サンプルソースでは、ワールド座標系のxMin, yMinを 0にしているため発現しませんが、以下のソースが正解です。 ご迷惑をおかけしました。
// ワールド座標系からデバイス座標系に変換する private int wcToDcX(double wX) { return (int)((wX - this.xMin) * scale); } private int wcToDcY(double wY) { return (int)((wY - this.yMin) * scale); } private int wcToDcLength(double wLength) { return (int)(wLength * this.scale); } // デバイス座標系からワールド座標系に変換する private double dcToWcX(int dX) { return dX / this.scale + this.xMin; } private double dcToWcY(int dY) { return dY / this.scale + this.yMin; }
第4刷では修正されています。
掲示板のご指摘より。
polylineButton = new Button("Polyine");
Polylineのスペルミスです。これに伴ってFig.3.16も間違っています。
4.2.4 (p.246)にあるとおり、interfaceにpublicは不要です(あっても構いませんが)。
他の箇所に合わせるなら、scaleではなくthis.scaleと書くべきですね……
誤
dcToWcX(e.getX()), dcToWcX(e.getY()));
正
dcToWcX(e.getX()), dcToWcY(e.getY()));
xMin, yMinを 0にしているため発現しませんが。
掲示板のご指摘より。
p.217 リスト左側の23行目〜
public void mouseDown(int x, int y) {} public void mouseUp(int x, int y) {} public void mouseDrag(int x, int y) {}
これらの空メソッドは不要です(どういう経緯で付けたのかは、既に思い出せません……)。