File "ObjectDraw.java"
Full Path: /home/analogde/www/Ebook/Informatique/JAVA/Source_TLS/Move/ObjectDraw.java
File size: 33.35 KB
MIME-type: text/plain
Charset: utf-8
/** file: ObjectDraw.java
* author: Robert M. Keller
* copyright 1997 by Robert M. Keller, all rights reserved
* purpose: Object-drawing applet (for pedagogical purposes)
*
* This is a simple drawing program. It illustrates several object-oriented
* ideas, including inheritance.
*
* The user is able to draw various shapes with the mouse. The shapes
* can be moved, grouped hierarchically, cut, and pasted, etc.
*
* The user can also have a textual description (S expression) of any
* object appear in a separate window.
*
* Currently there are five choice menus:
*
* Stay/Revert: With Revert, the program always goes back to Move
* mode after doing an operation. With Stay, it stays
* in the same mode.
*
* Mode: Selects between Move, Draw, Cut, Paste, etc.
*
* Shape: Selects the shape to be drawn.
*
* Fill: Selects whether the shape is filled or outline.
*
* Color: Selects the color
**/
/* $Revision: 1.5 $, $Date: 1997/10/16 00:37:15 $ */
/*
Method:
Each type of shape is a class, as defined by the following inheritance
hierarchy:
Rectangle
|
|
Shape
|
|
----------------------------------------
| | | |
| | | |
Box Oval Line Group
Rectangle is the one defined in package java.awt. It is not a graphic
object as such, but simply defines the rectangular region which bounds
the object. It is used to determine where the mouse is clicked (via
the 'inside' method).
Class Shape is a relatively abstract class. It carries values common to
all shape classes, such as color and filled.
Even though Line and Group ignore color and fill, it was decided for
simplicity not to make a separate node in the hierarchy for unfillable
objects.
A Group contains a set of shapes, currently represented using the
class java.util.Vector.
Continuous curves are represented by (generally large) groups of small
lines.
Each class has its own draw method, which translate into appropriate
calls for java.awt. In the case of Group, the draw method calls the
draw methods for each shape in the group.
When a shape is drawn, cut, etc. the entire image is redrawn. This
eliminates the need to have an erase method.
*/
import java.applet.*; // applet classes
import java.awt.*; // Abstract Window Toolkit classes
import java.util.*; // utility classes
/**
* ObjectDraw is the applet
**/
public class ObjectDraw extends Applet
{
Graphics graphics; // graphics buffer
Image image; // Image for graphics
Vector allShapes = new Vector(); // vector of all shapes in drawing
Shape currentShape; // the currently selected shape
Shape justCut; // the shape just cutd
boolean continuous; // used for free-form drawing
Vector curve;
int xDown, yDown; // where the mouse was pressed
int xCut, yCut; // where shape was cut
int grid = 1; // grid size
int maxGrid = 64; // maximum grid size
DefTable descriptions; // table of descriptions
DescriptionFrame descriptionFrame; // frame for descriptions
// widgets
static public Color backgroundColor = Color.white,
groupingColor = Color.black;
static public Font MainFont = new Font("Helvetica", Font.BOLD, 18);
Slider gridSlider // slider for grid size
= new Slider(this, "Grid", MainFont, 1, 1, maxGrid, 2);
Choice shapeChoice = new Choice(); // choice menu for shape
int chosenShape = BOX; // default for menu
int drawShape = chosenShape; // drawShape is shaped to be DRAWN
// (as opposed to for grouping)
Choice modeChoice = new Choice(); // choice menu for mode
int chosenMode = DRAW; // default mode
Choice colorChoice = new Choice(); // choice menu for color
Color chosenColor = Color.black; // default color
Vector allColors = new Vector(); // vector of all colors in menu
Choice fillChoice = new Choice(); // choice menu for fill
boolean fillMode = false;
Choice stayChoice = new Choice(); // choice menu for stay/revert
boolean stayMode = true;
// table of drawing modes and corresponding indices (for use in switches)
static String mode[] =
{ "Move", "Draw", "Cut", "CutAll", "Paste", "Copy", "Group",
"GroupAll", "Ungroup", "Lower", "Raise", "Describe" };
static final int
MOVE = 0, DRAW = 1, CUT = 2, CUTALL = 3, PASTE = 4, COPY = 5, GROUP = 6,
GROUPALL = 7, UNGROUP = 8, LOWER = 9, RAISE = 10, DESCRIBE = 11;
// table of shapes and corresponding indices (for use in switches)
static String shape[] = {"Box", "Line", "Curve", "Oval"};
static final int BOX = 0, LINE = 1, CURVE = 2, OVAL = 3;
/**
* Initialize the applet.
**/
public void init()
{
setLayout(new FlowLayout(FlowLayout.LEFT));
setBackground(backgroundColor); // set the background color
image = createImage(size().width, size().height);
graphics = image.getGraphics(); // make a graphics buffer
// set up widgets
stayChoice.setFont(MainFont); // add Stay/Revert menu
stayChoice.addItem("Stay");
stayChoice.addItem("Revert");
add(stayChoice);
modeChoice.setFont(MainFont); // add mode choice menu
for( int i = 0; i < mode.length; i++ ) // add mode choices
modeChoice.addItem(mode[i]);
setMode(chosenMode);
add(modeChoice); // add choice menu to applet
shapeChoice.setFont(MainFont); // add shape choice menu
for( int i = 0; i < shape.length; i++ ) // add shape choices
shapeChoice.addItem(shape[i]);
add(shapeChoice); // add shape menu to applet
fillChoice.setFont(MainFont);
fillChoice.addItem("NoFill");
fillChoice.addItem("Fill");
add(fillChoice); // add fill menu to applet
colorChoice.setFont(MainFont); // add color choice menu
addColor("Black", Color.black); // add colors to menu
addColor("Red", Color.red); // and to color String table
addColor("Blue", Color.blue);
addColor("Orange", Color.orange);
addColor("Yellow", Color.yellow);
addColor("Green", Color.green);
addColor("Cyan", Color.cyan);
addColor("Magenta", Color.magenta);
addColor("Gray", Color.gray);
addColor("DarkGray", Color.darkGray);
addColor("LightGray", Color.lightGray);
addColor("Pink", Color.pink);
addColor("White", Color.white);
add(colorChoice);
}
//
// Clear graphics
//
void clearGraphics()
{
graphics.clearRect(0, 0, size().width, size().height);
}
//
// Draw all items in the window.
//
void display()
{
for( Enumeration e = allShapes.elements(); e.hasMoreElements(); )
{
((Shape)e.nextElement()).draw(graphics);
}
graphics.setColor(Color.black);
graphics.drawRect(0, 0, size().width-1, size().height-1); // border
repaint();
}
//
// Redraw all items in the window.
//
void redisplay()
{
clearGraphics();
display();
}
//
// Start drawing a new shape.
//
void startShape(int x, int y, Color color, boolean fillMode)
{
switch( chosenShape )
{
case BOX: currentShape = new Box(x, y, color, fillMode); break;
case OVAL: currentShape = new Oval(x, y, color, fillMode); break;
case LINE: currentShape = new Line(x, y, color); break;
case CURVE: currentShape = new Line(x, y, color); break;
}
redisplay();
currentShape.draw(graphics);
repaint();
}
//
// Grow the current shape as the mouse is dragged.
//
void growShape(int x, int y)
{
redisplay();
currentShape.width = x - currentShape.x; // adjust size
currentShape.height = y - currentShape.y;
currentShape.draw(graphics);
repaint();
}
//
// Complete the current shape, adding it to allShapes.
//
void finishShape()
{
currentShape.complete();
allShapes.addElement(currentShape);
}
//
// Move the shape to another location.
//
void moveShape(int x, int y)
{
if( currentShape == null )
return;
currentShape.moveBy(x - xDown, y - yDown);
xDown = x;
yDown = y;
}
//
// Set the mode as specified and set the modeChoice menu
//
void setMode(int i)
{
switch( i )
{
case GROUPALL:
all();
group(2);
i = chosenMode; // don't change mode for GroupAll
break;
case CUTALL:
all();
group(1);
if( currentShape == null )
break;
cut(currentShape.x, currentShape.y);
i = chosenMode; // don't change mode for CutAll
redisplay();
break;
}
chosenMode = i;
modeChoice.select(mode[i]);
}
//
// Look for shape at a given position, setting currentShape to one found.
// Return true if found, otherwise false.
//
boolean findShape(int x, int y)
{
for (int i = allShapes.size()-1; i >= 0; i-- ) // start at end of Vector
{
Shape s = (Shape)allShapes.elementAt(i);
if( s.inside(x, y) )
{
currentShape = s;
return true;
}
}
currentShape = null;
return false;
}
//
// Move current shape so that it is displayed first, selected last.
//
void lower()
{
if( currentShape == null )
return;
allShapes.removeElement(currentShape);
allShapes.insertElementAt(currentShape, 0);
}
//
// Move current shape so that it is displayed last, selected first.
//
void raise()
{
if( currentShape == null )
return;
allShapes.removeElement(currentShape);
allShapes.addElement(currentShape);
}
//
// Cut the selected shape.
//
void cut(int x, int y)
{
if( currentShape == null )
return;
xCut = x;
yCut = y;
allShapes.removeElement(currentShape);
justCut = currentShape;
}
//
// Paste pastes the most recently cut shape, if any.
// with x and y specifying an offset from that position
void paste(int x, int y)
{
if( justCut == null )
return;
currentShape = justCut.copy();
allShapes.addElement(currentShape);
currentShape.moveBy(x, y);
}
//
// Group surrounded shapes into one.
//
void group(int minGroupSize)
{
Vector members = establishGroup(currentShape);
if( members.size() < minGroupSize )
{
// a little warning: don't make groups of size 0 or 1
System.out.println("group of size " + members.size() + " not created");
currentShape = null;
return;
}
// remove members in group from allShapes
for( Enumeration e = members.elements(); e.hasMoreElements(); )
allShapes.removeElement(e.nextElement());
// make the group the current shape
currentShape = new Group(currentShape.x, currentShape.y, this, members);
allShapes.addElement(currentShape);
}
//
// Ungroup selected shape if it is a group.
//
void ungroup()
{
if( currentShape == null )
return;
if( !(currentShape instanceof Group) )
return;
allShapes.removeElement(currentShape);
Group group = (Group)currentShape;
if( group.shared )
{
// clone Vector first so as not to mess up sharing
group = group.deepCopy();
}
// add component members to allShapes, setting their coordinates
for( Enumeration e = group.members.elements(); e.hasMoreElements();)
{
Shape s = ((Shape)e.nextElement());
s.moveBy(currentShape.x, currentShape.y);
allShapes.addElement(s);
}
// no shape is current after ungrouping
currentShape = null;
}
//
// Make a Vector of the shapes contained inside shape s.
//
Vector establishGroup(Shape s)
{
Vector v = new Vector();
for( Enumeration e = allShapes.elements(); e.hasMoreElements(); )
{
Shape t = (Shape)e.nextElement();
// check whether t is inside boundary of s
if( t.x >= s.x
&& t.y >= s.y
&& t.x + t.width <= s.x + s.width
&& t.y + t.height <= s.y + s.height )
{
v.addElement(t); // put t on new Vector
}
}
return v;
}
// All makes a shape which surrounds all shapes (e.g. for grouping)
void all()
{
currentShape = new Box(0, 0, groupingColor, false);
currentShape.width = size().width;
currentShape.height = size().height;
}
//
// revert sets the mode back to Move if stayMode is false
//
void revert()
{
if( !stayMode )
{
setMode(MOVE);
}
}
/**
* mouseDown is called when the mousebutton is depressed.
**/
public boolean mouseDown(Event e, int x, int y)
{
x = (x/grid)*grid;
y = (y/grid)*grid;
xDown = x; yDown = y;
switch( chosenMode )
{
case MOVE:
findShape(x, y);
break;
case DRAW:
continuous = false;
chosenShape = drawShape;
if( chosenShape == CURVE )
{
continuous = true;
curve = new Vector();
}
startShape(x, y, chosenColor, fillMode);
break;
case GROUP:
chosenShape = BOX;
startShape(x, y, groupingColor, false);
break;
case DESCRIBE:
findShape(x, y);
if( currentShape == null )
break;
if( descriptionFrame == null )
{
descriptionFrame = new DescriptionFrame(this);
}
// create description table
descriptions = new DefTable();
// clear text of frame, then add descriptions
descriptionFrame.clearText();
currentShape.describe(descriptionFrame.shape, this);
descriptions.describe(descriptionFrame.defs, this);
descriptionFrame.update();
break;
}
return true;
}
/**
* mouseDrag is called when the mouse is dragged.
**/
public boolean mouseDrag(Event e, int x, int y)
{
x = (x/grid)*grid;
y = (y/grid)*grid;
switch( chosenMode )
{
case MOVE:
moveShape(x, y);
redisplay();
break;
case DRAW:
if( continuous )
{
// finish this shape and start a new one, adding to curve
growShape(x, y);
finishShape();
curve.addElement(currentShape);
startShape(x, y, chosenColor, fillMode);
}
else
{
growShape(x, y);
}
break;
case GROUP:
growShape(x, y);
break;
}
return true;
}
/**
* mouseUp is called when the mousebutton is released.
**/
public boolean mouseUp(Event v, int x, int y)
{
x = (x/grid)*grid;
y = (y/grid)*grid;
switch( chosenMode )
{
case MOVE:
currentShape = null;
break;
case DRAW:
finishShape();
if( continuous )
{
// finish the curve
curve.addElement(currentShape);
currentShape =
new Group(currentShape.x, currentShape.y, this, curve);
// remove members in group from allShapes
for( Enumeration e = curve.elements(); e.hasMoreElements(); )
allShapes.removeElement(e.nextElement());
allShapes.addElement(currentShape);
}
break;
case CUT:
if( findShape(x, y) )
cut(x, y);
break;
case PASTE:
paste(x-xCut, y-yCut);
break;
case COPY:
if( findShape(x, y) )
{
cut(x, y);
paste(0, 0);
paste(grid, -grid);
}
break;
case GROUP:
group(2);
break;
case UNGROUP:
findShape(x, y);
ungroup();
break;
case LOWER:
findShape(x, y);
lower();
findShape(x, y);
break;
case RAISE:
findShape(x, y);
raise();
findShape(x, y);
break;
}
revert();
redisplay();
return true;
}
/**
* action handles events targeted for buttons, etc.
* These events do not go to mouseUp etc.
**/
public boolean action(Event event, Object arg)
{
if( event.target == stayChoice )
{
stayMode = ((String)arg == "Stay");
}
else if( event.target == modeChoice )
{
setMode(findString((String)arg, mode));
}
else if( event.target == shapeChoice )
{
drawShape = chosenShape = findString((String)arg, shape);
setMode(DRAW); // setting shape assumes user wants to draw
}
else if( event.target == fillChoice )
{
fillMode = ((String)arg == "Fill" );
setMode(DRAW); // setting fill assumes user wants to draw
}
else if( event.target == colorChoice )
{
chosenColor = colorFromString((String)arg);
setMode(DRAW); // setting color assumes user wants to draw
}
return super.action(event, arg); // Delegate all other actions to super.
}
/**
* Set mode or perform action when user moves a slider
**/
public boolean handleEvent(Event event)
{
if( event.target == gridSlider.scroll )
{
grid = (int)gridSlider.getValue(); // grid slider
return true;
}
return super.handleEvent(event); // Delegate all other actions to super.
}
/**
* update is implicitly called by repaint()
* It calls paint(Graphics)
**/
public void update(Graphics g)
{
paint(g);
}
/**
* paint(Graphics) is is called by update(Graphics)
**/
public void paint(Graphics g)
{
g.drawImage(image, 0, 0, null);
g.setColor(Color.black);
}
// create an entry in String-Color correspondence table
void addColor(String name, Color color)
{
colorChoice.addItem(name);
allColors.addElement(new Pair(name, color));
}
// return a Color object given a String naming the color
public Color colorFromString(String string)
{
for( Enumeration e = allColors.elements(); e.hasMoreElements(); )
{
Pair p = (Pair)(e.nextElement());
if( string.equals(p.first) )
return (Color)(p.second);
}
System.err.println("*** color not understood: " + string + " using black");
return Color.black;
}
// return a String from a Color object
public String colorToString(Color color)
{
for( Enumeration e = allColors.elements(); e.hasMoreElements(); )
{
Pair p = (Pair)(e.nextElement());
if( color.equals(p.second) )
return (String)(p.first);
}
return color.toString();
}
static int findString(String toBeFound, String array[])
{
for( int i = 0; i < array.length; i++ )
{
if( toBeFound.equals(array[i]) )
return i;
}
return -1;
}
} // ObjectDraw
//
// The abstract class Shape
//
abstract class Shape extends java.awt.Rectangle
{
Color color; // the color of the shape
boolean filled; // whether the shape is filled or outline
// Constructor; width and height are set when the shape size is finally
// determined
Shape(int x, int y, Color color, boolean filled)
{
this.x = x;
this.y = y;
this.color = color;
this.filled = filled;
}
Shape(Shape orig) // Copy constructor: Create a copy of a shape
{
x = orig.x;
y = orig.y;
width = orig.width;
height = orig.height;
color = orig.color;
filled = orig.filled;
}
// move shape by indicated increment
void moveBy(int dx, int dy)
{
x += dx;
y += dy;
}
// complete deals with negative widths and heights when a shape is finished
// It is overridden for Box and Oval and is a no-op for Line and Group.
void complete()
{ }
abstract Shape copy(); // returns copy of shape
abstract void draw(int x, int y, Graphics g); // draws with added offset x, y
abstract void describe(int indentation, StringBuffer buff, ObjectDraw app);
void draw(Graphics g) // draw with no offset
{
draw(0, 0, g);
}
void describe(StringBuffer buff, ObjectDraw app) // print with no indentation
{
describe(0, buff, app);
}
static void indent(int indentation, StringBuffer buff)
{
for( int i = 0; i < indentation; i++ )
buff.append(" ");
}
} // Shape
//
// A Box shape
//
class Box extends Shape
{
Box(int x, int y, Color color, boolean filled)
{
super(x, y, color, filled);
}
Box(Box box) // copy constructor
{
super((Shape)box);
}
Shape copy()
{
return new Box(this);
}
void draw(int x, int y, Graphics g) // draw a Box
{
// note that x, y, width, and height are local
x = this.x + x; // change from offset to absolute
y = this.y + y;
int width = this.width;
int height = this.height;
if( width < 0 ) // accomodate negative widths and heights
{
width = -width;
x -= width;
}
if( height < 0 )
{
height = -height;
y -= height;
}
g.setColor(color);
if( filled )
g.fillRect(x, y, width, height);
else
g.drawRect(x, y, width, height);
}
// complete fixes width and height if either is negative.
void complete()
{
if( width < 0 )
{
x += width;
width = -width;
}
if( height < 0 )
{
y += height;
height = -height;
}
}
void describe(int indentation, StringBuffer buff, ObjectDraw app)
{
indent(indentation, buff);
buff.append("(" + (filled ? "filled " : "")
+ "rectangle " + app.colorToString(color) + " "
+ x + " " + y + " "
+ width + " " + height + ")\n");
}
} // Box
//
// an Oval Shape
//
class Oval extends Shape
{
Oval(int x, int y, Color color, boolean filled)
{
super(x, y, color, filled);
}
Oval(Oval oval) // copy constructor
{
super((Shape)oval);
}
Shape copy()
{
return new Oval(this);
}
void draw(int x, int y, Graphics g) // draw an Oval
{
// note that x, y, width, and height are local
x = this.x + x; // change from offset to absolute
y = this.y + y;
int width = this.width;
int height = this.height;
if( width < 0 ) // accomodate negative widths and heights
{
width = -width;
x -= width;
}
if( height < 0 )
{
height = -height;
y -= height;
}
g.setColor(color);
if( filled )
g.fillOval(x, y, width, height);
else
g.drawOval(x, y, width, height);
}
// complete fixes width and height if either is negative.
void complete()
{
if( width < 0 )
{
x += width;
width = -width;
}
if( height < 0 )
{
y += height;
height = -height;
}
}
void describe(int indentation, StringBuffer buff, ObjectDraw app)
{
indent(indentation, buff);
buff.append("(" + (filled ? "filled " : "")
+ "oval " + app.colorToString(color) + " "
+ x + " " + y + " "
+ width + " " + height + ")\n");
}
} // Oval
//
// A straight line
//
class Line extends Shape
{
Line(int x, int y, Color color)
{
super(x, y, color, false); // not filled
}
Line(Line line) // copy constructor
{
super((Shape)line);
}
Shape copy()
{
return new Line(this);
}
void draw(int x, int y, Graphics g) // draw a Line
{
g.setColor(color);
g.drawLine(this.x + x, this.y + y, this.x+x+width, this.y+y+height);
}
// inside is determined specially for a line, since negative width and
// height are permitted.
public boolean inside(int x, int y)
{
int x0 = this.x;
int y0 = this.y;
int width = this.width;
int height = this.height;
if( width < 0 )
{
width = -width;
x0 -= width;
}
if( height < 0 )
{
height = -height;
y0 -= height;
}
return x >= x0
&& x < x0 + width
&& y >= y0
&& y < y0 + height;
}
void describe(int indentation, StringBuffer buff, ObjectDraw app)
{
indent(indentation, buff);
buff.append("(line " + app.colorToString(color) + " "
+ x + " " + y + " "
+ width + " " + height + ")\n");
}
} // Line
//
// A Group is a set of shapes treated as a single Shape.
// Groups may have Groups as elements, etc.
// Groups ignore the color attribute of Shape
//
class Group extends Shape
{
Vector members; // members of the group
boolean shared = false; // indicates whether members shared
Group(int x, int y, ObjectDraw app, Vector shapes)
{
super(x, y, null, false); // no color, not filled
members = initMembers(shapes, app.currentShape);
}
Group(Group group) // copy constructor
{ // note: does not run initMembers
super((Shape)group);
members = group.members;
shared = group.shared = true; // indicate sharing
}
Shape copy()
{
return new Group(this);
}
Group deepCopy()
{
Group result = (Group)copy();
Vector old = result.members;
result.members = new Vector();
for( Enumeration e = old.elements(); e.hasMoreElements(); )
{
result.members.addElement(((Shape)e.nextElement()).copy());
}
return result;
}
// initialize the members of a group by setting their coordinates to be
// relative.
Vector initMembers(Vector members, Shape wrapper)
{
Vector v = new Vector();
// find maxes and mins
int xmin = x+wrapper.width, ymin = y+wrapper.height, xmax = -1, ymax = -1;
for( Enumeration e = members.elements(); e.hasMoreElements(); )
{
Shape s = (Shape)e.nextElement();
if( s.width >= 0 )
{
xmin = Math.min(xmin, s.x);
xmax = Math.max(xmax, s.x+s.width-1);
}
else
{
xmin = Math.min(xmin, s.x+s.width+1);
xmax = Math.max(xmax, s.x);
}
if( s.height >= 0 )
{
ymin = Math.min(ymin, s.y);
ymax = Math.max(ymax, s.y+s.height-1);
}
else
{
ymin = Math.min(ymin, s.y+s.height+1);
ymax = Math.max(ymax, s.y);
}
}
x = xmin;
y = ymin;
width = xmax - xmin + 1;
height = ymax - ymin + 1;
// adjust origins
for( Enumeration e = members.elements(); e.hasMoreElements(); )
{
Shape s = (Shape)e.nextElement();
s.x -= xmin;
s.y -= ymin;
v.addElement(s);
}
return v;
}
// draw a Group by drawing its members, with offset
void draw(int x, int y, Graphics g)
{
for( Enumeration e = members.elements(); e.hasMoreElements(); )
{
((Shape)e.nextElement()).draw(this.x + x, this.y + y, g);
}
}
void describe(int indentation, StringBuffer buff, ObjectDraw app)
{
indent(indentation, buff);
buff.append("(group " + x + " " + y);
if( shared )
{
int i = app.descriptions.getRef(members);
buff.append(" (ref " + i +"))\n");
}
else
{
buff.append("\n");
for( Enumeration e = members.elements(); e.hasMoreElements(); )
{
((Shape)(e.nextElement())).describe(indentation+2, buff, app);
}
indent(indentation, buff);
buff.append(")\n");
}
}
} // Group
// Pair is just a pair of two objects
class Pair
{
Object first;
Object second;
Pair(Object first, Object second)
{
this.first = first;
this.second = second;
}
} // Pair
/**
* A DefTable is a table of object definitions
**/
class DefTable extends java.util.Vector
{
// getRef gets a table index for a Vector of shapes
int getRef(Vector members)
{
int i = indexOf(members);
if( i < 0 )
{
i = size();
// definition not already in table, put there now
addElement(members);
}
return i;
}
// show all definitions in table
void describe(StringBuffer buff, ObjectDraw app)
{
int i = 0;
for( Enumeration e = elements(); e.hasMoreElements(); )
{
buff.append("(def " + i +"\n");
describe1(i++, (Vector)e.nextElement(), buff, app);
buff.append(")\n");
}
}
// show one definition in table
void describe1(int i, Vector V, StringBuffer buff, ObjectDraw app)
{
for( Enumeration e = V.elements(); e.hasMoreElements(); )
{
((Shape)e.nextElement()).describe(2, buff, app);
}
}
} // DefTable
/**
* A DescriptionFrame is a frame containing text descriptions
**/
class DescriptionFrame extends Frame
{
StringBuffer shape; // description of a shape
StringBuffer defs; // auxiliary definitions
TextArea textArea = new TextArea(); // text area within frame
Button clearButton = new Button("Clear"); // button to clear description
Choice fontChoice = new Choice();
static int fontSize[] = {8, 10, 12, 14, 18, 24}; // font sizes available
DescriptionFrame(ObjectDraw app) // constructor
{
super("Description");
setLayout(new FlowLayout(FlowLayout.LEFT));
add(clearButton);
// establish fontChoice menu
for( int i = 0; i < fontSize.length; i++ )
fontChoice.addItem(new Integer(fontSize[i]).toString());
fontChoice.select("18");
textArea.setFont(new Font("Helvetica", Font.BOLD, 18));
fontChoice.setFont(app.MainFont); // Font of the menu, not selected font
add(fontChoice);
// establish textArea
setBackground(app.backgroundColor);
add(textArea);
resize(app.size().width, 2*app.size().height/3);
clearButton.setFont(app.MainFont); // Font of the button
}
void clearText()
{
shape = new StringBuffer();
defs = new StringBuffer();
}
void update()
{
textArea.appendText(defs.toString());
textArea.appendText(shape.toString());
show();
}
public boolean action(Event event, Object arg)
{
if( event.target == clearButton )
{
textArea.setText(""); // clear description text
}
else if( event.target == fontChoice )
{
textArea.setFont(new Font("Helvetica",
Font.BOLD,
new Integer((String)arg).intValue()));
}
return super.action(event, arg); // Delegate all other actions to super.
}
} // DescriptionFrame
/**
* A Slider is a combination of a Label, a Scrollbar, and a TextField.
**/
class Slider
{
double Value; // Value maintained by the slider
Label label; // label for the slider
Scrollbar scroll; // the slider itself
TextField field; // field showing value of slider
/**
* Create a slider.
**/
Slider(Applet app, // Applet in which to place slider
String lab, // Label for the slider
Font font, // Font for Label and TextField
double initial, // Initial value
int min, // minimum value of the slider
int max, // maximum
int fieldSize) // number of characters in TextField
{
label = new Label(lab);
label.setFont(font);
app.add(label); // Add the label.
scroll = new Scrollbar(Scrollbar.VERTICAL, (int)initial, 100, min, max);
scroll.setFont(font);
app.add(scroll); // Add the scrollbar.
this.Value = initial; // Initialize the value.
field = new TextField(fieldSize);
field.setText(new Double(initial).toString());
field.setFont(font);
field.setEditable(false);
app.add(field); // Add the TextField.
}
/**
* Set the value of the slider programmatically.
**/
void setValue(double Value)
{
this.Value = Value;
field.setText(new Double(Value).toString());
}
/**
* Update the value of the slider when the scrollbar is adjusted
* and return the value (must be called by handleEvent).
**/
double getValue()
{
setValue(scroll.getValue());
return Value;
}
} // class Slider