4、使用Graphics来创建图形

    在SWT中,graphic context(GC)可以单独创建为一个对象,也可以通过PaintEvent获得。但是在Draw2d中,Figure
  可以从2.1节中讲述的那些paint方法中获得Graphics(方法参数)。Graphics的绝大多数方法都和CG中的相同,最大
  的不同是Draw2d允许Graphics对象利用translate()方法移动。
    然而,Draw2d提供了更为强大的功能来创建和操纵几何图形(Shapes)。它有好几个包、很多类用来处理几何图形。
  4.1、使用Graphics类
    如前所述,Graphics类的方法和SWT的GC基本一样,所以,在这一小节里,我们将创建图C.2种列出的那些组件,即
  如下三种Figure:
    1)DecisionFigure——即程序流程图中的分支结构,一个输入,两个输出(是/否)。
    2)ProcessFigure——流程图中的某个处理,一个输入,一个输出。
    3)TerminatorFigure——表示一个程序流程的开始或者结束,只有一个输入(结束)或输出(开始)。
    对于我们的以上三种Figure,他们的大小由一个叫做size的变量来控制,他们上面显示的文字由一个叫做message
  的String来设置。这些限制都来自于Figure对象的外部,所以暂时你还看不到。
    这些类的代码如下:(列表C.3至C.5)

列表C.3:DecisionFigure.java
package com.swtjface.AppC;
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
public class DecisionFigure extends ActivityFigure
{
FixedAnchor inAnchor, yesAnchor, noAnchor;
public DecisionFigure()
{
inAnchor = new FixedAnchor(this);
inAnchor.place = new Point(1, 0);
targetAnchors.put("in_dec",inAnchor);
noAnchor = new FixedAnchor(this);
noAnchor.place = new Point(2, 1);
sourceAnchors.put("no",noAnchor);
yesAnchor = new FixedAnchor(this);
yesAnchor.place = new Point(1, 2);
sourceAnchors.put("yes",yesAnchor);
}
public void paintFigure(Graphics g)
{
Rectangle r = bounds;
PointList pl = new PointList(4);
pl.addPoint(r.x + r.width/2, r.y);
pl.addPoint(r.x, r.y + r.height/2);
pl.addPoint(r.x + r.width/2, r.y + r.height-1);
pl.addPoint(r.x + r.width, r.y + r.height/2);
g.drawPolygon(pl);
g.drawText(message, r.x+r.width/4+5, r.y+3*r.height/8);
g.drawText("N", r.x+7*r.width/8, r.y+3*r.height/8);
g.drawText("Y", r.x+r.width/2-2, r.y+3*r.height/4);
}
}
    由于分支结构不是规则图形(它是个棱形),无法使用既有图形,所以我们需要指定一系列点来描绘它;谢天谢地,
  ProcessFigure是一个矩形(Rectangle),所以容易创建地多。
    列表C.4:ProcessFigure.java
package com.swtjface.AppC;
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
public class ProcessFigure extends ActivityFigure
{
FixedAnchor inAnchor, outAnchor;
public ProcessFigure()
{
inAnchor = new FixedAnchor(this);
inAnchor.place = new Point(1, 0);
targetAnchors.put("in_proc", inAnchor);
outAnchor = new FixedAnchor(this);
outAnchor.place = new Point(1, 2);
sourceAnchors.put("out_proc", outAnchor);
}
public void paintFigure(Graphics g)
{
Rectangle r = bounds;
g.drawText(message, r.x + r.width/4, r.y + r.height/4);
g.drawRectangle(r.x, r.y, r.width-1, r.height-1);
}
}
    TerminatorFigure的左右两条边是圆弧,所以比ProcessFigure要麻烦点,但代码却是不难理解的。
    列表C.5:TerminatorFigure.java
package com.swtjface.AppC;
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
public class TerminatorFigure extends ActivityFigure
{
FixedAnchor inAnchor, outAnchor;
public TerminatorFigure()
{
inAnchor = new FixedAnchor(this);
inAnchor.place = new Point(1, 0);
targetAnchors.put("in_term",inAnchor);
outAnchor = new FixedAnchor(this);
outAnchor.place = new Point(1, 2);
sourceAnchors.put("out_term",outAnchor);
}
public void paintFigure(Graphics g)
{
Rectangle r = bounds;
g.drawArc(r.x + r.width/8, r.y, r.width/4, r.height-1, 90, 180);
g.drawLine(r.x + r.width/4, r.y, r.x + 3*r.width/4, r.y);
g.drawLine(r.x + r.width/4, r.y + r.height-1, r.x + 3*r.width/4,
r.y + r.height-1);
g.drawArc(r.x + 5*r.width/8, r.y, r.width/4, r.height-1, 270, 180);
g.drawText(message, r.x+3*r.width/8, r.y+r.height/8);
}
}


    显然,这些类不会只负责将图形画出来就完事了,还有两个更复杂的方面:一是这些Figure需要连接到其他的Figure
  上去,这需要使用ConnectionAnchors(叫做FixedAnchors)。第二,这些图形将来在讲述GEF的时候还要用到,很
  多方法现在解释还为时过早(作者这里主要指拖拽、编辑上面的文字等功能)。
    由于所有这三个类都继承了ActivityFigure,所以现在该它登场了。这个基类包含上述三个Figure的共性。如列表
  C.6所示,他的大部分方法用来跟踪Connections以及Connections的anchors。
    列表C.6:ActivityFigure.java

package com.swtjface.AppC;
import org.eclipse.draw2d.*;
import org.eclipse.draw2d.geometry.*;
import java.util.*;
abstract public class ActivityFigure
extends Figure
{
  Rectangle r = new Rectangle();
  Hashtable targetAnchors = new Hashtable();
  Hashtable sourceAnchors = new Hashtable();
  String message = new String();
  public void setName(String msg)
  {
    message = msg;
    repaint();
  }
  public ConnectionAnchor ConnectionAnchorAt(Point p)
  {
    ConnectionAnchor closest = null;
    long min = Long.MAX_VALUE;
    Hashtable conn = getSourceConnectionAnchors();
    conn.putAll(getTargetConnectionAnchors());
    Enumeration e = conn.elements();
    while (e.hasMoreElements())
    {
      ConnectionAnchor c = (ConnectionAnchor) e.nextElement();
      Point p2 = c.getLocation(null);
      long d = p.getDistance2(p2);
      if (d < min)
      {
        min = d;
        closest = c;
      }
    }
    return closest;
  }  public ConnectionAnchor getSourceConnectionAnchor(String name)
  {
    return (ConnectionAnchor)sourceAnchors.get(name);
  }  public ConnectionAnchor getTargetConnectionAnchor(String name)
  {
    return (ConnectionAnchor)targetAnchors.get(name);
  }  public String getSourceAnchorName(ConnectionAnchor c)
  {
    Enumeration enum = sourceAnchors.keys();
    String name;
    while (enum.hasMoreElements())
    {
      name = (String)enum.nextElement();
      if (sourceAnchors.get(name).equals(c))
        return name;
    }
    return null;
  }  public String getTargetAnchorName(ConnectionAnchor c)
  {
    Enumeration enum = targetAnchors.keys();
    String name = null;
    while (enum.hasMoreElements())
    {
      name = (String)enum.nextElement();
      if (targetAnchors.get(name).equals(c))
        return name;
    }
    return null;
  }  public ConnectionAnchor getSourceConnectionAnchorAt(Point p)
  {
    ConnectionAnchor closest = null;
    long min = Long.MAX_VALUE;
    Enumeration e = getSourceConnectionAnchors().elements();
    while (e.hasMoreElements())
    {
      ConnectionAnchor c = (ConnectionAnchor) e.nextElement();
      Point p2 = c.getLocation(null);
      long d = p.getDistance2(p2);
      if (d < min)
      {
        min = d;
        closest = c;
      }
    }
    return closest;
  }  public Hashtable getSourceConnectionAnchors()
  {
    return sourceAnchors;
  }  public ConnectionAnchor getTargetConnectionAnchorAt(Point p)
  {
    ConnectionAnchor closest = null;
    long min = Long.MAX_VALUE;
    Enumeration e = getTargetConnectionAnchors().elements();
    while (e.hasMoreElements())
    {
      ConnectionAnchor c = (ConnectionAnchor) e.nextElement();
      Point p2 = c.getLocation(null);
      long d = p.getDistance2(p2);
      if (d < min)
      {
        min = d;
        closest = c;
      }
    }
    return closest;
  }  public Hashtable getTargetConnectionAnchors()
  {
    return targetAnchors;
  }
}

    对于ConnectionAnchor和Connection,我们将稍后再讨论。现在让我们看一下Draw2d的几何包。
  4.2、Draw2d中的几何和图论    你已经看过如何使用Point和Rectangle,但Draw2d还提供了其他更多的类来处理几何图形。例如更高精度的类
  PrecisionPoint,PrecisionRectangle,以及PrecisionDimension。此外还有Ray对象,其作用就像数学中的向量
  一样;以及Transform类,用来实现移动、旋转、缩放等功能。
    包org.eclipse.draw2d.graph里面有一些用来创建和分析有向图的工具类,例如基本的Node和Edge,以及特有的
  布局管理器DirectedGraphLayout。图论已远超本书范围,但如果你对此有兴趣,这个包应该是很有用的。
    Draw2d的Figure之间并非是用Edge连接的,而是用Connection,下面来讨论这个类。