// 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(); } }