acm.program.GraphicsProgram Maven / Gradle / Ivy
Show all versions of javakarel Show documentation
/*
* @(#)GraphicsProgram.java 1.99.1 08/12/08
*/
// ************************************************************************
// * Copyright (c) 2008 by the Association for Computing Machinery *
// * *
// * The Java Task Force seeks to impose few restrictions on the use of *
// * these packages so that users have as much freedom as possible to *
// * use this software in constructive ways and can make the benefits of *
// * that work available to others. In view of the legal complexities *
// * of software development, however, it is essential for the ACM to *
// * maintain its copyright to guard against attempts by others to *
// * claim ownership rights. The full text of the JTF Software License *
// * is available at the following URL: *
// * *
// * http://www.acm.org/jtf/jtf-software-license.pdf *
// * *
// ************************************************************************
// REVISION HISTORY
//
// -- V2.0 --
// Code cleanup 30-Jan-07 (ESR)
// 1. Renamed instance variable "listener" to "eventListener" to avoid
// warning messages from some compilers.
// 2. Removed unnecessary startHook code.
package acm.program;
import acm.graphics.*;
import acm.util.*;
import java.awt.*;
import java.awt.event.*;
import java.lang.reflect.*;
import java.util.*;
/**
* This class is a standard subclass of Program
* whose principal window is used for drawing graphics.
*/
public abstract class GraphicsProgram extends Program {
/* Constructor: GraphicsProgram() */
/**
* Creates a new graphics program.
*
* Example: GraphicsProgram program = new GraphicsProgram();
*/
protected GraphicsProgram() {
eventListener = new GProgramListener(this);
gc = createGCanvas();
gc.addMouseListener(eventListener);
if (eventListener.needsMouseMotionListeners()) {
gc.addMouseMotionListener(eventListener);
}
add(gc, CENTER);
validate();
}
/* Method: run() */
/**
* Specifies the code to be executed as the program runs.
* The run
method is required for applications that have
* a thread of control that runs even in the absence of user actions,
* such as a program that uses console interation or that involves
* animation. GUI-based programs that operate by setting up an initial
* configuration and then wait for user events usually do not specify a
* run
method and supply a new definition for init
* instead.
*/
public void run() {
/* Empty */
}
/* Method: init() */
/**
* Specifies the code to be executed as startup time before the
* run
method is called. Subclasses can override this
* method to perform any initialization code that would ordinarily
* be included in an applet init
method. In general,
* subclasses will override init
in GUI-based programs
* where the program simply sets up an initial state and then waits
* for events from the user. The run
method is required
* for applications in which there needs to be some control thread
* while the program runs, as in a typical animation.
*
* Example: program.init();
*/
public void init() {
/* Empty */
}
/* Method: getGCanvas() */
/**
* Returns the GCanvas
object used by this program.
*
* Example: GCanvas gc = getGCanvas();
* @return The GCanvas
object used by the program
*/
public GCanvas getGCanvas() {
return gc;
}
/* Method: add(gobj) */
/**
* Adds a new graphical object to this container.
*
* Example: add(gobj);
* @param gobj The graphical object to add
*/
public void add(GObject gobj) {
gc.add(gobj);
}
/* Method: add(gobj, x, y) */
/**
* Adds the graphical object to the canvas and sets its location
* to the point (x
, y
).
*
* Example: add(gobj, x, y);
* @param gobj The graphical object to add
* @param x The new x-coordinate for the object
* @param y The new y-coordinate for the object
*/
public final void add(GObject gobj, double x, double y) {
gobj.setLocation(x, y);
add(gobj);
}
/* Method: add(gobj, pt) */
/**
* Adds the graphical object to the canvas and sets its location to the specified point.
*
* Example: add(gobj, pt);
* @param gobj The graphical object to add
* @param pt The new coordinates of the point
*/
public final void add(GObject gobj, GPoint pt) {
gobj.setLocation(pt);
add(gobj);
}
/* Method: add(comp, x, y) */
/**
* Adds the component to the canvas and sets its location
* to the point (x
, y
).
*
* Example: add(comp, x, y);
* @param comp The component to add
* @param x The new x-coordinate for the object
* @param y The new y-coordinate for the object
*/
public final void add(Component comp, double x, double y) {
comp.setLocation(GMath.round(x), GMath.round(y));
gc.add(comp);
}
/* Method: add(comp, pt) */
/**
* Adds the component to the canvas and sets its location to the specified point.
*
* Example: add(comp, pt);
* @param comp The component to add
* @param pt A GPoint
object giving the coordinates of the point
*/
public final void add(Component comp, GPoint pt) {
add(comp, pt.getX(), pt.getY());
}
/* Method: remove(gobj) */
/**
* Removes a graphical object from this container.
*
* Example: remove(gobj);
* @param gobj The graphical object to remove
*/
public void remove(GObject gobj) {
gc.remove(gobj);
}
/* Method: removeAll() */
/**
* Removes all graphical objects from this container. Note that this
* definition overrides the Container
version of
* removeAll
, which is replaced by
* removeAllComponents
.
*
* Example: removeAll();
*/
public void removeAll() {
gc.removeAll();
}
/* Method: getElementCount() */
/**
* Returns the number of graphical objects stored in this GCanvas
.
*
* Example: int n = getElementCount();
* @return The number of graphical objects in this GCanvas
*/
public int getElementCount() {
return gc.getElementCount();
}
/* Method: getElement(index) */
/**
* Returns the graphical object at the specified index, numbering from back
* to front in the the z dimension.
*
* Example: GObject gobj = getElement(index);
* @param index The index of the component to return
* @return The graphical object at the specified index
*/
public GObject getElement(int index) {
return gc.getElement(index);
}
/* Method: getElementAt(x, y) */
/**
* Returns the topmost graphical object that contains the point
* (x
, y
), or null
if no such
* object exists.
*
* Example: GObject gobj = program.getElementAt(x, y);
* @param x The x-coordinate of the point being tested
* @param y The y-coordinate of the point being tested
* @return The graphical object at the specified location, or null
* if no such object exists.
*/
public GObject getElementAt(double x, double y) {
return gc.getElementAt(x, y);
}
/* Method: getElementAt(pt) */
/**
* Returns the topmost graphical object that contains the specified point,
* or null
if no such object exists.
*
* Example: GObject gobj = program.getElementAt(pt);
* @param pt The coordinates being tested
* @return The graphical object at the specified location, or null
* if no such object exists
*/
public final GObject getElementAt(GPoint pt) {
return getElementAt(pt.getX(), pt.getY());
}
/* Method: iterator() */
/**
* Returns an Iterator
that cycles through the elements within
* this container in the default direction, which is from back to front.
* You can also run the iterator in the opposite direction by using the
* iterator
(
* direction)
form of this method.
*
* Applets that want to run in browsers, however, should avoid using
* this method, because Iterator
is not supported on 1.1 browsers.
* For maximum portability, you should rely instead on the
* getElementCount
* and getElement
methods,
* which provide the same functionality in a browser-compatible way.
*
* Example: Iterator i = iterator();
* @return An Iterator
ranging over the elements of the
* container from back to front
*/
public Iterator iterator() {
return gc.iterator();
}
/* Method: iterator(direction) */
/**
* Returns an Iterator
that cycles through the elements
* within this container in the specified direction, which must be one
* of the constants FRONT_TO_BACK
* or BACK_TO_FRONT
* from the GContainer
interface.
*
* for (Iterator<GObject> i = iterator(direction); i.hasNext(); )
*
*
Applets that want to run in browsers, however, should avoid using
* this method, because Iterator
is not supported on 1.1 browsers.
* For maximum portability, you should rely instead on the
* getElementCount
* and getElement
methods,
* which provide the same functionality in a browser-compatible way.
*
* Example: Iterator i = iterator(direction);
* @param direction direction no idea
* @return An Iterator
ranging over the elements of the
* container in the specified direction
*/
public Iterator iterator(int direction) {
return gc.iterator(direction);
}
/* Method: addMouseListeners() */
/**
* Adds the program as both a MouseListener
and MouseMotionListener
* to the canvas.
*
* Example: addMouseListeners();
*/
public void addMouseListeners() {
gc.addMouseListener(this);
gc.addMouseMotionListener(this);
}
/* Method: addMouseListeners(listener) */
/**
* Adds the specified listener as a MouseListener
and/or
* MouseMotionListener
, as appropriate, to the canvas.
*
* Example: addMouseListeners(listener);
* @param listener A listener object that is either a MouseListener
, a
* MouseMotionListener
, or both
*/
public void addMouseListeners(EventListener listener) {
boolean ok = false;
if (listener instanceof MouseListener) {
gc.addMouseListener((MouseListener) listener);
ok = true;
}
if (listener instanceof MouseMotionListener) {
gc.addMouseMotionListener((MouseMotionListener) listener);
ok = true;
}
if (!ok) throw new ErrorException("addMouseListeners: Illegal listener");
}
/* Method: addKeyListeners() */
/**
* Adds the program as a KeyListener
to the canvas.
*
* Example: addKeyListeners();
*/
public void addKeyListeners() {
gc.addKeyListener(this);
}
/* Method: addKeyListeners(listener) */
/**
* Adds the specified listener as a KeyListener
to the canvas.
*
* Example: addKeyListeners(listener);
* @param listener A KeyListener
object
*/
public void addKeyListeners(KeyListener listener) {
gc.addKeyListener(listener);
}
/* Method: waitForClick() */
/**
* Waits for a mouse click in the window before proceeding.
*
* Example: waitForClick();
*/
public void waitForClick() {
eventListener.waitForClick();
}
/* Method: repaint() */
/**
* Signals a need to repaint this window.
*
*/
public void repaint() {
gc.repaint();
super.repaint();
}
/* Method: removeAllComponents() */
/**
* Removes all components from this container.
*
* Example: removeAllComponents();
*
*/
public void removeAllComponents() {
super.removeAll();
}
/* Override method: setBackground(bg) */
/**
* Sets the background color of the GCanvas
.
*
* Example: setBackground(bg);
* @param bg The new background color
*
*/
public void setBackground(Color bg) {
super.setBackground(bg);
if (gc != null) gc.setBackground(bg);
}
/* Static method: startGraphicsProgram(gobj, args) */
/**
* Creates a GraphicsProgram
containing the specified GObject
* and then starts it. This code is called only by the start
method in
* GObject
.
*
* Example: startGraphicsProgram(gobj, args);
* @param gobj The object to be inserted into the GraphicsProgram
* @param args The array of arguments
*
*/
public static void startGraphicsProgram(GObject gobj, String[] args) {
GraphicsProgram program = new GObjectProgram();
program.setStartupObject(gobj);
program.start(args);
}
/* Inherited method: print(value) */
/**
* @inherited Program#void print(String value)
* Displays the argument value on the console, leaving the cursor at the end of
* the output.
*/
/* Inherited method: println() */
/**
* @inherited Program#void println()
* Advances the console cursor to the beginning of the next line.
*/
/* Inherited method: println(value) */
/**
* @inherited Program#void println(String value)
* Displays the argument value on the console and then advances the cursor
* to the next line.
*/
/* Inherited method: readLine() */
/**
* @inherited Program#String readLine()
* Reads and returns a line of input from the console.
*/
/* Inherited method: readLine(prompt) */
/**
* @inherited Program#String readLine(String prompt)
* Prompts the user for a line of input.
*/
/* Inherited method: readInt() */
/**
* @inherited Program#int readInt()
* Reads and returns an integer value from the user.
*/
/* Inherited method: readInt(prompt) */
/**
* @inherited Program#int readInt(String prompt)
* Prompts the user to enter an integer.
*/
/* Inherited method: readDouble() */
/**
* @inherited Program#double readDouble()
* Reads and returns a double-precision value from the user.
*/
/* Inherited method: readDouble(prompt) */
/**
* @inherited Program#double readDouble(String prompt)
* Prompts the user to enter a double-precision number, which is
* returned as the value of this method.
*/
/* Inherited method: readBoolean() */
/**
* @inherited Program#boolean readBoolean()
* Reads and returns a boolean value (true
or false
).
*/
/* Inherited method: readBoolean(prompt) */
/**
* @inherited Program#boolean readBoolean(String prompt)
* Prompts the user to enter a boolean value.
*/
/* Inherited method: readBoolean(prompt, trueLabel, falseLabel) */
/**
* @inherited Program#boolean readBoolean(String prompt, String trueLabel, String falseLabel)
* Prompts the user to enter a boolean value, which is matched against the
* labels provided.
*/
/* Inherited method: getConsole() */
/**
* @inherited Program#IOConsole getConsole()
* Returns the console associated with this program.
*/
/* Inherited method: getDialog() */
/**
* @inherited Program#IODialog getDialog()
* Returns the dialog used for user interaction.
*/
/* Inherited method: getReader() */
/**
* @inherited Program#BufferedReader getReader()
* Returns a BufferedReader
whose input comes from the console.
*/
/* Inherited method: getWriter() */
/**
* @inherited Program#PrintWriter getWriter()
* Returns a PrintWriter
whose output is directed to the console.
*/
/* Inherited method: setTitle(title) */
/**
* @inherited Program#void setTitle(String title)
* Sets the title of this program.
*/
/* Inherited method: getTitle() */
/**
* @inherited Program#String getTitle()
* Gets the title of this program.
*/
/* Inherited method: pause(milliseconds) */
/**
* @inherited Program#void pause(double milliseconds)
* Delays the calling thread for the specified time, which is expressed in
* milliseconds.
*/
/* Factory method: createGCanvas() */
/**
* Creates the GCanvas
used by the GraphicsProgram
. Subclasses can
* override this method to create their own GCanvas
types.
*
* Example: GCanvas gc = program.createGCanvas();
* @return The GCanvas
to be inserted into the program
*
*/
protected GCanvas createGCanvas() {
return new GCanvas();
}
/* Protected method: endHook() */
/**
* Ensures that the window is repainted at the end of the program.
*/
protected void endHook() {
gc.repaint();
}
/* Protected method: isStarted() */
/**
* Checks to see whether this program has started, usually by checking to see
* whether some pane exists. Subclasses can override this method to ensure
* that their structures are visible before proceeding.
*
*/
protected boolean isStarted() {
if (gc == null || !super.isStarted()) return false;
Dimension size = gc.getSize();
return (size != null) && (size.width != 0) && (size.height != 0);
}
/* Private instance variables */
private GCanvas gc;
private GProgramListener eventListener;
}
/* Package class: GProgramListener */
/**
* The GProgramListener
class implements the waitForClick
* method and the objectdraw-style listener model.
*/
class GProgramListener implements MouseListener, MouseMotionListener {
/* Constructor: GProgramListener() */
/**
* Creates the GProgramListener
.
*/
public GProgramListener(GraphicsProgram program) {
myProgram = program;
try {
Class> programClass = program.getClass();
Class[] types = { Class.forName("acm.graphics.GPoint") };
try {
mousePressedHook = programClass.getMethod("mousePressed", types);
} catch (NoSuchMethodException ex) {
/* Empty */
}
try {
mouseReleasedHook = programClass.getMethod("mouseReleased", types);
} catch (NoSuchMethodException ex) {
/* Empty */
}
try {
mouseClickedHook = programClass.getMethod("mouseClicked", types);
} catch (NoSuchMethodException ex) {
/* Empty */
}
try {
mouseMovedHook = programClass.getMethod("mouseMoved", types);
} catch (NoSuchMethodException ex) {
/* Empty */
}
try {
mouseDraggedHook = programClass.getMethod("mouseDragged", types);
} catch (NoSuchMethodException ex) {
/* Empty */
}
} catch (Exception ex) {
throw new ErrorException(ex);
}
}
/* Method: needsMouseMotionListeners() */
/**
* Returns true if this listener has to respond to mouse motion events as well.
*/
public boolean needsMouseMotionListeners() {
return mouseMovedHook != null || mouseDraggedHook != null;
}
/* Method: mouseClicked() */
/**
* Called by the event-handling system when the mouse is clicked in the canvas.
*/
public void mouseClicked(MouseEvent e) {
if (mouseClickedHook != null) {
Object[] args = { new GPoint(e.getX(), e.getY()) };
try {
mouseClickedHook.invoke(myProgram, args);
} catch (Exception ex) {
throw new ErrorException(ex);
}
}
signalClickOccurred();
}
/* Method: mousePressed() */
/**
* Called by the event-handling system when the mouse button is pressed.
*/
public void mousePressed(MouseEvent e) {
if (mousePressedHook != null) {
Object[] args = { new GPoint(e.getX(), e.getY()) };
try {
mousePressedHook.invoke(myProgram, args);
} catch (Exception ex) {
throw new ErrorException(ex);
}
}
}
/* Method: mouseReleased() */
/**
* Called by the event-handling system when the mouse button is released.
*/
public void mouseReleased(MouseEvent e) {
if (mouseReleasedHook != null) {
Object[] args = { new GPoint(e.getX(), e.getY()) };
try {
mouseReleasedHook.invoke(myProgram, args);
} catch (Exception ex) {
throw new ErrorException(ex);
}
}
}
/* Method: mouseEntered() */
/**
* Called by the event-handling system when the mouse enters the component.
*/
public void mouseEntered(MouseEvent e) {
/* Empty */
}
/* Method: mouseExited() */
/**
* Called by the event-handling system when the mouse leaves the component.
*/
public void mouseExited(MouseEvent e) {
/* Empty */
}
/* Method: mouseMoved() */
/**
* Called by the event-handling system when the mouse moves.
*/
public void mouseMoved(MouseEvent e) {
if (mouseMovedHook != null) {
Object[] args = { new GPoint(e.getX(), e.getY()) };
try {
mouseMovedHook.invoke(myProgram, args);
} catch (Exception ex) {
throw new ErrorException(ex);
}
}
}
/* Method: mouseDragged() */
/**
* Called by the event-handling system when the mouse is dragged with the button down.
*/
public void mouseDragged(MouseEvent e) {
if (mouseDraggedHook != null) {
Object[] args = { new GPoint(e.getX(), e.getY()) };
try {
mouseDraggedHook.invoke(myProgram, args);
} catch (Exception ex) {
throw new ErrorException(ex);
}
}
}
/* Method: waitForClick() */
/**
* Waits for a mouse click in the window before proceeding.
*
* Example: waitForClick();
*/
public synchronized void waitForClick() {
clickFlag = false;
while (!clickFlag) {
try {
wait();
} catch (InterruptedException ex) {
/* Empty */
}
}
}
/* Private method: signalClickOccurred() */
/**
* Notifies any waiting objects that a click has occurred.
*/
private synchronized void signalClickOccurred() {
clickFlag = true;
notifyAll();
}
/* Private instance variables */
private GraphicsProgram myProgram;
private Method mousePressedHook;
private Method mouseReleasedHook;
private Method mouseClickedHook;
private Method mouseMovedHook;
private Method mouseDraggedHook;
private boolean clickFlag;
}
/* Package class: GObjectProgram */
/**
* This class is used to launch a program containing a single GObject
* instance at its center.
*/
class GObjectProgram extends GraphicsProgram {
/* Hook method: runHook() */
/**
* Calls the run method in the graphical object.
*/
protected void runHook() {
GObject gobj = (GObject) getStartupObject();
GDimension size = gobj.getSize();
add(gobj, (getWidth() - size.getWidth()) / 2, (getHeight() - size.getHeight()) / 2);
try {
Class> gobjClass = gobj.getClass();
String className = gobjClass.getName();
className = className.substring(className.lastIndexOf(".") + 1);
setTitle(className);
Method run = gobjClass.getMethod("run", new Class[0]);
if (run == null) throw new ErrorException(className + " has no run method");
run.invoke(gobj, new Object[0]);
} catch (Exception ex) {
throw new ErrorException(ex);
}
}
}