// A simple drawing window class.
// (c) 1998 duane a. bailey
package element;
import java.awt.*;
import java.awt.event.*;
/**
* A simple drawing window class. Allows the programmer to construct
* a free standing or contained slate to draw on.
* @author (c) 1998 duane a. bailey
*/
public class DrawingWindow
{
/**
* The container that surrounds the drawing window.
*/
private Container frame;
/**
* The desired width and height of the window. May differ if the
* window is contained by an independent container.
*/
private int width, height;
/**
* The canvas associated with this drawing window.
* Is responsible for maintaining the offscreen bitmap and the
* association between the mouse and the window.
*/
private DrawingCanvas canvas;
/**
* The font associated with the drawing window.
* Usually a fixed width, courier-like font is best.
*/
private Font font;
/**
* The backing store for the window. Helps to double buffer.
*/
private Graphics offscreen; // off screen graphics associated with osi
/**
* A context for finding out information about fonts on this device.
*/
protected static Graphics exampleContext;
/**
* The independent thread associated with this window.
*/
private Thread thread; // independent thread for window
/**
* The colors associated with the background and foreground.
* Usually java.awt.Color.white and java.awt.Color.black, by default.
*/
private Color backColor, foreColor; // colors for drawing
/**
* The current position of the drawing cursor.
*/
private Pt currentPosition; // where the current position is
/**
* Whether or not we're painting (drawing) or inverting.
*/
private int mode; // current painting mode
/**
* The saved context, when the context must be changed internally.
* Only one context can be saved/restored at a time.
*/
private Color savedBackColor, savedForeColor; // colors for drawing
/**
* Saved current position.
*/
private Pt savedCurrentPosition; // where the current position is
/**
* Saved mode.
*/
private int savedMode; // current painting mode
/**
* The constants that are used to locally encode paint/invert mode.
*/
final static private int PAINT = 0; // draw by setting pixels
final static private int INVERT = 1; // draw by inverting pixels
/**
* Constructs a 200x200 standalone drawing window.
*/
public DrawingWindow()
// post: constructs a 200x200 standalone drawing window
{
this(200,200);
}
/**
* Construct a 200x200 drawing window within another container.
* Size is only approximate, especially if the window is smaller than
* the container.
* @param c the container that holds the drawing window.
*/
public DrawingWindow(Container c)
{
this(200,200,c);
}
/**
* Construct a free-standing drawing window of a particular size.
* @param width the desired width of the window.
* @param height the desired height of the window.
*/
public DrawingWindow(int width, int height)
// post: constructs a drawing window of desired dimensions
{
this(width,height,"Drawing Window");
}
/**
* Construct a contained drawing window of a particular size.
* @param width the desired width of the window
* @param height the desired height of the window
* @param c the frame/panel/applet that contains the window
*/
public DrawingWindow(int width, int height, Container c)
// pre: 0 <= width, height; container is valid
// post: constructs a window contained in another container
{
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
frame = c;
canvas = new DrawingCanvas(width,height);
frame.add("Center", canvas);
currentPosition = new Pt(0,0);
savedCurrentPosition = new Pt();
//frame.show();
this.width=width;
this.height=height;
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
// this call blocks until the window can be displayed:
offscreen = canvas.getOffscreenGraphics();
if (exampleContext == null) exampleContext = offscreen;
font = new Font("Courier",Font.PLAIN,12);
offscreen.setFont(font);
setForeground(Color.black);
setBackground(Color.white);
paintMode();
}
/**
* Construct a drawing window with a paritcular dimension and title
* @param width the desired width of the window
* @param height the desired height of the window
* @param title the title of the window
*/
public DrawingWindow(int width, int height, String title)
// pre: 0 <= width, height; title is a valid string
// post: constructs a named window with desired dimensions
{
Thread.currentThread().setPriority(Thread.NORM_PRIORITY);
Frame f = new Frame(title);
frame = f;
canvas = new DrawingCanvas(width,height);
frame.add("Center", canvas);
f.pack();
this.width=width; this.height=height;
// frame.setSize(width,height);
currentPosition = new Pt(0,0);
savedCurrentPosition = new Pt();
frame.setVisible(true);
Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
// this call blocks until the window can be displayed:
offscreen = canvas.getOffscreenGraphics();
if (exampleContext == null) exampleContext = offscreen;
font = new Font("Courier",Font.PLAIN,12);
offscreen.setFont(font);
setForeground(Color.black);
setBackground(Color.white);
paintMode();
}
/**
* Save the current graphics context.
* One may be saved at a time.
*/
private void save()
// pre: no useful graphics context is currently saved
// post: saves the current graphics context
{
savedForeColor = foreColor;
savedBackColor = backColor;
savedCurrentPosition = new Pt(currentPosition);
savedMode = mode;
}
/**
* Restore the saved graphics context.
*/
private void restore()
// pre: graphics context was saved.
// post: restored the context that was saved.
{
// we conditionally update this, for performance.
if (foreColor != savedForeColor) setForeground(savedForeColor);
if (backColor != savedBackColor) setBackground(savedBackColor);
currentPosition = savedCurrentPosition;
if (mode != savedMode) {
if (savedMode == PAINT) paintMode();
else invertMode();
}
}
/**
* Hold the updating process until the matching release.
* May be nested.
*/
public void hold()
// post: delays updating the screen until matching release
{
canvas.hold();
}
/**
* Release the updating process, allowing real-time drawing to the
* window. May be nested. Drawing resumes only when all holds have been
* released.
*/
public void release()
// post: releases a hold on the drawing window
{
canvas.release();
}
/**
* Determine the current size of the drawing window's canvas.
* @return the rect describing the current drawing canvas size.
*/
public Rect bounds()
// post: returns rectangle describing bounds of window
{
return new Rect(0,0,width,height);
}
/**
* Get the current mouse position as a Pt.
* @return Pt describing the current mouse position.
*/
public Pt getMouse()
// post: returns the current mouse position as a Pt
{
return canvas.getMouse();
}
/**
* Return true iff the mouse is currently pressed.
* @return true iff the mouse is currently pressed.
*/
public boolean mousePressed()
// post: returns true if mouse is pressed
{
return canvas.mousePressed();
}
/**
* Waits for the mouse to be pressed.
* @return the point of the press.
*/
public Pt awaitMousePress()
// post: blocks until mouse is pressed;
// returns point of press
{
return canvas.awaitMousePress();
}
/**
* Waits for the mouse to be released.
* @return the point of the mouse release.
*/
public Pt awaitMouseRelease()
// post: blocks until mouse is released;
// returns point of release
{
return canvas.awaitMouseRelease();
}
/**
* post: blocks until mouse is released
* returns point of press
*
*/
public Pt awaitMouseClick()
// post: blocks until mouse is released;
// returns point of press
{
canvas.awaitMousePress();
return canvas.awaitMouseRelease();
}
/**
* Wait for a key to be pressed.
* @return the character associated with the key press.
*/
public char awaitKey()
// post: blocks until a keystroke
// returns character associated with key
{
return canvas.awaitKey();
}
/**
* Moves the current position to (x,y).
* No marks are made to the screen.
* @param x the new current horizontal position
* @param y the new current vertical position
*/
public void moveTo(int x, int y)
// post: sets the current position to (x,y)
{
currentPosition.moveTo(x,y);
}
/**
* Moves the current position to p.
* No marks are made to the screen.
* @param p the new current position.
*/
public void moveTo(Pt p)
// post: sets the current position to point p
{
currentPosition.moveTo(p);
}
/**
* Moves to a position relative to the current position.
* No marks are made to the screen
*
* @param dx the horizontal offset to the new current position.
* @param dy the vertical offset to the new current position.
*/
public void move(int dx, int dy)
// pre: current position has been set previously
// post: changes the current position by (dx,dy)
{
currentPosition.move(dx,dy);
}
/**
* Draws a line from the current position to a point (x,y).
* Afterwards, (x,y) becomes the new current position.
*
* @param x the horizontal component of the far endpoint
* @param y the vertical component of the far endpoint
* @see #moveTo
*/
public void lineTo(int x, int y)
// post: draws a line from current position to (x,y);
// (x,y) becomes the new current position
{
drawLine(currentPosition.x(), currentPosition.y(), x, y);
currentPosition.moveTo(x,y);
}
/**
* Draws a line segment from the current position to p.
* Afterwards, p becomes the new current position.
* @param p the far endpoint of the line segment.
* @see #moveTo
*/
public void lineTo(Pt p)
// pre: the current position has previously been set
// post: draws a line from current position to (x,y)
{
lineTo(p.x(),p.y());
}
/**
* Draws a line relative to the current position.
*
* @param dx the horizontal span of the line segment
* @param dy the vertical span of the line segment
*/
public void line(int dx, int dy)
// pre: the current position has been set
// post: draws a line relative from current position by (dx,dy)
{
lineTo(currentPosition.x()+dx, currentPosition.y()+dy);
}
/**
* Draw a single point at (x,y). (x,y) becomes the current position.
* @param x the horizontal coordinate of the point
* @param y the vertical coordinate of the point
*/
public void drawPt(int x, int y)
// post: draws the point (x,y). (x,y) becomes the current position.
{
drawLine(x,y,x,y);
}
/**
* Fills the point at (x,y). (x,y) becomes the current position.
* Same as drawPt.
* @param x the horizontal coordinate of the point
* @param y the vertical coordinate of the point
* @see #drawPt
*/
public void fillPt(int x, int y)
// post: draws the point (x,y). (x,y) becomes the current position.
{
fillLine(x,y,x,y);
}
/**
* Erases the point at (x,y). (x,y) becomes the current position.
* @param x the horizontal coordinate of the point
* @param y the vertical coordinate of the point
*/
public void clearPt(int x, int y)
// post: erases the screen at (x,y)
{
clearLine(x,y,x,y);
}
/**
* Draws a line from (x0,y0) to (x1,y1).
*
* @param x0 the horizontal position of one end of line
* @param y0 the vertical position of one end of the line
* @param x1 the horizontal position of other end of line
* @param y1 the vertical position of other end of the line
*/
public void drawLine(int x0, int y0, int x1, int y1)
// post: draws a line from (x0, y0) to (x1, y1)
{
offscreen.drawLine(x0,y0,x1,y1);
canvas.repaint(x0,y0,x1,y1);
}
/**
* Draws a line from src to dest.
*
* @param src the beginning of the line segment
* @param dest the end of the line segment
*/
public void drawLine(Pt src, Pt dest)
// post: draws a line from src to dest
{
drawLine(src.x(),src.y(), dest.x(),dest.y());
}
/**
* Fills the line from src to dest.
*
* @param x0 the horizontal position of one end of line
* @param y0 the vertical position of one end of the line
* @param x1 the horizontal position of other end of line
* @param y1 the vertical position of other end of the line
* @see #drawLine
*/
public void fillLine(int x0, int y0, int x1, int y1)
// post: draws a line from (x0, y0) to (x1, y1)
{
drawLine(x0,y0,x1,y1);
}
/**
* Draws a line from src to dest.
*
* @param src the beginning of the line segment
* @param dest the end of the line segment
* @see #drawLine
*/
public void fillLine(Pt src, Pt dest)
// post: draws a line from src to dest
{
drawLine(src, dest);
}
/**
* Erases a line from (x0,y0) to (x1,y1).
*
* @param x0 the horizontal position of one end of line
* @param y0 the vertical position of one end of the line
* @param x1 the horizontal position of other end of line
* @param y1 the vertical position of other end of the line
*/
public void clearLine(int x0, int y0, int x1, int y1)
// post: erases a line from (x0, y0) to (x1, y1)
{
save();
paintMode();
setForeground(backColor);
drawLine(x0,y0,x1,y1);
canvas.repaint(x0,y0,x1,y1);
restore();
}
/**
* Erases a line from (x0, y0) to (x1, y1)
*
* @param src the beginning of the line segment
* @param dest the end of the line segment
*/
public void clearLine(Pt src, Pt dest)
// post: draws a line from (x0, y0) to (x1, y1)
{
clearLine(src.x(),src.y(), dest.x(),dest.y());
}
/**
* Sets the foreground color for the window to c.
* Colors may be found in java.awt.Color
* @param c a java.awt.Color describing the color of drawing
*/
public void setForeground(java.awt.Color c)
// pre: c is a valid Color
// post: the pen is filled with that color for drawing
{
offscreen.setColor(c);
foreColor = c;
}
/**
* Sets the background color for the window to c.
* Background color is used for erasing.
* Colors may be found in java.awt.Color
* @param c a java.awt.Color describing the color of drawing
*/
public void setBackground(Color c)
// pre: c is a valid Color
// post: the eraser is filled with that color for erasing
{
backColor = c;
// update color inverting
if (mode == INVERT) invertMode();
canvas.setBackground(c);
}
/**
* Sets the drawing mode to accumulate drawings on the screen.
* Pixels drawn on screen have color of the foreground.
* This is the default drawing mode.
* @see #invertMode
*/
public void paintMode()
// post: sets the drawing mode to paint
{
mode = PAINT;
offscreen.setPaintMode();
}
/**
* Sets the drawing mode to invert the drawings on the screen.
* Pixels drawn on the screen have color that is opposite the color
* found on the screen beforehand.
* @see #paintMode
*/
public void invertMode()
// post: sets the drawing mode to invert
{
mode = INVERT;
offscreen.setXORMode(backColor);
}
/**
* Fills in a general drawable object.
* In invert mode, the object is inverted.
* @param d the drawable object to be filled in.
*/
public void fill(Drawable d)
// pre: d is a valid drawable object
// post: fills in the drawable object, d
{
d.fillOn(this);
}
/**
* Erases a general drawable object.
* @param d the drawable object to be erased.
*/
public void clear(Drawable d)
// pre: d is a valid drawable object
// post: the drawable object is erased
{
d.clearOn(this);
}
/**
* Draws a general drawable object.
* In invert mode, the object is inverted.
* @param d the general object to be drawn.
*/
public void draw(Drawable d)
// pre: d is a general drawable object
// post: object d drawn (or inverted) on the drawing window
{
d.drawOn(this);
}
/**
* Draw the rectangle described by (x,y,width,height)
* @param x the left coordinate of the rectangle
* @param y the top coordinate of the rectangle
* @param width the width of the rectangle
* @param height the height of the rectangle
*/
public void drawRect(int x, int y, int width, int height)
// pre: 0 <= width, height
// post: the rectangle is drawn onto the screen
{
offscreen.drawRect(x,y,width,height);
canvas.repaint(x,y,x+width,y+height);
}
/**
* Fill in the rectangle described by (x,y,width,height).
* @param x the left coordinate of the rectangle.
* @param y the top coordinate of the rectangle
* @param width the width of the rectangle to be drawn
* @param height the height of the rectangle to be drawn
*/
public void fillRect(int x, int y, int width, int height)
// pre: 0 <= width, height
// post: fills in (or inverts) the rectangle on the screen.
{
offscreen.fillRect(x,y,width,height);
canvas.repaint(x,y,x+width,y+height);
}
/**
* Erase the rectangle described by (x,y,width,height).
* @param x the left coordinate of the rectangle.
* @param y the top coordinate of the rectangle
* @param width the width of the rectangle to be drawn
* @param height the height of the rectangle to be drawn
*/
public void clearRect(int x, int y, int width, int height)
// pre: 0 <= width, height
// post: erases the rectangle on the screen.
{
offscreen.clearRect(x,y,width,height);
canvas.repaint(x,y,x+width,x+height);
}
/**
* Draw (or invert) the rounded rectangle.
* @param x the left coordinate of the rounded rectangle.
* @param y the top coordinate of the rounded rectangle
* @param width the width of the rounded rectangle to be drawn
* @param height the height of the rounded rectangle to be drawn
* @param arcwidth the bounding box width of the arc used to make the corners
* @param archeight the bounding box height of the arc used to make the corners
*/
public void drawRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
// pre: 0 <= width, height, arcwidth, archeight
// post: draws the rounded rectangle on the screen.
{
offscreen.drawRoundRect(x,y,width,height,arcWidth,arcHeight);
canvas.repaint(x,y,x+width,y+height);
}
/**
* Fill (or invert) the rounded rectangle.
* @param x the left coordinate of the rounded rectangle.
* @param y the top coordinate of the rounded rectangle
* @param width the width of the rounded rectangle to be drawn
* @param height the height of the rounded rectangle to be drawn
* @param arcwidth the bounding box width of the arc used to make the corners
* @param archeight the bounding box height of the arc used to make the corners
*/
public void fillRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
// pre: 0 <= width, height, arcwidth, archeight
// post: fills the rounded rectangle on the screen.
{
offscreen.fillRoundRect(x,y,width,height,arcWidth,arcHeight);
canvas.repaint(x,y,x+width,y+height);
}
/**
* Erase a rounded rectangle.
* @param x the left coordinate of the rounded rectangle.
* @param y the top coordinate of the rounded rectangle
* @param width the width of the rounded rectangle to be drawn
* @param height the height of the rounded rectangle to be drawn
* @param arcwidth the bounding box width of the arc used to make the corners
* @param archeight the bounding box height of the arc used to make the corners
*/
public void clearRoundRect(int x, int y, int width, int height, int arcWidth, int arcHeight)
// pre: 0 <= width, height, arcwidth, archeight
// post: erases the rounded rectangle on the screen.
{
save();
paintMode();
setForeground(backColor);
fillRoundRect(x,y,width,height,arcWidth,arcHeight);
restore();
}
/**
* Draw the oval described by the rectangle (x,y,width,height)
* @param x the left coordinate of the oval
* @param y the top coordinate of the oval
* @param width the width of the oval
* @param height the height of the oval
*/
public void drawOval(int x, int y, int width, int height)
// pre: 0 <= width, height
// post: the oval is drawn (or inverted) onto the screen
{
offscreen.drawOval(x,y,width,height);
canvas.repaint(x,y,x+width,y+height);
}
/**
* Fill the oval described by the rectangle (x,y,width,height)
* @param x the left coordinate of the oval
* @param y the top coordinate of the oval
* @param width the width of the oval
* @param height the height of the oval
*/
public void fillOval(int x, int y, int width, int height)
// pre: 0 <= width, height
// post: the oval is filled (or inverted) onto the screen
{
offscreen.fillOval(x,y,width,height);
canvas.repaint(x,y,x+width,y+height);
}
/**
* Erase the oval described by the rectangle (x,y,width,height)
* @param x the left coordinate of the oval
* @param y the top coordinate of the oval
* @param width the width of the oval
* @param height the height of the oval
*/
public void clearOval(int x, int y, int width, int height)
// pre: 0 <= width, height
// post: the oval is erased from the screen
{
save();
paintMode();
setForeground(backColor);
fillOval(x,y,width,height);
restore();
}
/**
* Draw the circle described by (x,y,radius)
* @param x the left coordinate of the circle
* @param y the top coordinate of the circle
* @param radius the radius of the circle
*/
public void drawCircle(int x, int y, int radius)
// pre: 0 <= radius
// post: the circle is drawn onto the screen
{
drawOval(x,y,radius*2,radius*2);
}
/**
* Fill in (or invert) the circle described by (x,y,radius)
* @param x the left coordinate of the circle
* @param y the top coordinate of the circle
* @param radius the radius of the circle
*/
public void fillCircle(int x, int y, int radius)
// pre: 0 <= radius
// post: the circle is filled in on the screen
{
fillOval(x,y,radius*2,radius*2);
}
/**
* Erase the circle described by (x,y,radius)
* @param x the left coordinate of the circle
* @param y the top coordinate of the circle
* @param radius the radius of the circle
*/
public void clearCircle(int x, int y, int radius)
// pre: 0 <= radius
// post: the circle is erased from the screen
{
clearOval(x,y,radius*2,radius*2);
}
/**
* Draw (or invert) a portion of an oval.
* All angles are measured in degrees, with zero pointing to the
* right, and increasing counterclockwise.
* @param x the left coordinate of the rectangle that describes the oval
* @param y the top coordinate of the rectangle that describes the oval
* @param width the width of the oval to be drawn
* @param height the height of the oval
* @param sa the starting angle of the arc
* @param da the angle spanned by the arc
*/
public void drawArc(int x, int y, int width, int height, int sa, int da)
// pre: 0 <= width, height
// post: draws (or inverts) arc on the screen
{
offscreen.drawArc(x,y,width,height,sa,da);
canvas.repaint(x,y,x+width,y+height);
}
/**
* Fill (or invert) a solid portion of an oval.
* All angles are measured in degrees, with zero pointing to the
* right, and increasing counterclockwise.
* @param x the left coordinate of the rectangle that describes the oval
* @param y the top coordinate of the rectangle that describes the oval
* @param width the width of the oval to be drawn
* @param height the height of the oval
* @param sa the starting angle of the arc
* @param da the angle spanned by the arc
*/
public void fillArc(int x, int y, int width, int height, int sa, int da)
// pre: 0 <= width, height
// post: fills (or inverts) solid arc on the screen
{
offscreen.fillArc(x,y,width,height,sa,da);
canvas.repaint(x,y,x+width,y+height);
}
/**
* Erase a portion of an oval.
* All angles are measured in degrees, with zero pointing to the
* right, and increasing counterclockwise.
* @param x the left coordinate of the rectangle that describes the oval
* @param y the top coordinate of the rectangle that describes the oval
* @param width the width of the oval to be drawn
* @param height the height of the oval
* @param sa the starting angle of the arc
* @param da the angle spanned by the arc
*/
public void clearArc(int x, int y, int width, int height, int sa, int da)
// pre: 0 <= width, height
// post: erases arc on the screen
{
save();
paintMode();
setForeground(backColor);
fillArc(x,y,width,height,sa,da);
canvas.repaint(x,y,x+width,y+height);
restore();
}
/**
* Draws a text string at the point (x,y)
* @param s the text to be drawn
* @param x the horizontal location of the text
* @param y the vertical location of the text
*/
public void drawString(java.lang.String s, int x, int y)
// pre: s is a valid string
// post: the string s is drawn with lower-left coordinate at (x,y)
{
drawText(new Text(s,x,y));
}
/**
* Draws a positioned text element on the screen.
* @param t a text element that describe the location of a string
*/
public void drawText(Text t)
// pre: t is a valid text element
// post: draws the text element t on the screen
{
offscreen.setFont(t.font());
offscreen.drawString(t.string(),t.left(),t.bottom());
canvas.repaint(t.left(),t.top(),t.width(),t.height());
}
/**
* Draws a positioned text element on the screen.
* @param t a text element that describe the location of a string
* @see #drawText
*/
public void fillText(Text t)
// pre: t is a valid text element
// post: draws the text element t on the screen
{
drawText(t);
}
/**
* Erases a positioned text element on the screen.
* @param t a text element that describe the location of a string
*/
public void clearText(Text t)
// pre: t is a valid text element
// post: erases the text element t from the screen
{
save();
setForeground(backColor);
drawText(t);
canvas.repaint(t.left(),t.top(),t.width(),t.height());
restore();
}
}