acm.graphics.GCanvas Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of javakarel Show documentation
Show all versions of javakarel Show documentation
This the original Stanford Karel for Java, packaged for Maven. ACM Library is included. See also https://cs.stanford.edu/people/eroberts/karel-the-robot-learns-java.pdf
The newest version!
/*
* @(#)GCanvas.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 --
// Bug fix 27-Jul-06 (ESR, JTFBug 2006-001, reported by Chris Nevison)
// 1. Fixed implementation of enabledList.
//
// Bug fix 26-Apr-07 (ESR, JTFBug 2007-005, reported by Leland Beck)
// 1. Removed cross-file reference to GMouseEvent.
//
// Code cleanup 28-May-07 (ESR)
// 1. Added generic type tags.
// 2. Substituted GObjectList for ArrayList.
// 3. Removed warnings about use of Iterator.
//
// Feature enhancement 26-May-08 (ESR)
// 1. Added support for serialization.
package acm.graphics;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
/* Class: GCanvas */
/**
* The GCanvas
class is a lightweight component that also
* serves as a container for graphical objects. As such, this class
* provides the link between graphical objects and the window system.
* Conceptually, the GCanvas
provides a background canvas
* to which other graphical objects can be added.
*/
public class GCanvas extends Container implements GContainer {
/* Constructor: GCanvas() */
/**
* Creates a new GCanvas
that contains no objects.
*
* Example: GCanvas gc = new GCanvas();
*/
public GCanvas() {
contents = new GObjectList(this);
setBackground(Color.WHITE);
setForeground(Color.BLACK);
setOpaque(true);
setAutoRepaintFlag(true);
setNativeArcFlag(false);
setLayout(null);
gCanvasListener = new GCanvasListener(this);
addComponentListener(gCanvasListener);
addMouseListener(gCanvasListener);
addMouseMotionListener(gCanvasListener);
}
/* Method: add(gobj) */
/**
* Adds the graphical object to this canvas.
*
* Example: gc.add(gobj);
* @param gobj The graphical object to add
*/
public void add(GObject gobj) {
contents.add(gobj);
conditionalRepaint();
}
/* Method: add(gobj, x, y) */
/**
* Adds the graphical object to this canvas and sets its location
* to the point (x
, y
).
*
* Example: gc.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 this canvas and sets its location to the specified point.
*
* Example: gc.add(gobj, pt);
* @param gobj The graphical object to add
* @param pt A GPoint
object giving the coordinates of the point
*/
public final void add(GObject gobj, GPoint pt) {
add(gobj, pt.getX(), pt.getY());
}
/* Method: remove(gobj) */
/**
* Removes a graphical object from this GCanvas
.
*
* Example: gc.remove(gobj);
* @param gobj The graphical object to remove
*/
public void remove(GObject gobj) {
contents.remove(gobj);
conditionalRepaint();
}
/* Method: removeAll() */
/**
* Removes all graphical objects from this GCanvas
.
*
* Example: gc.removeAll();
*/
public void removeAll() {
contents.removeAll();
super.removeAll();
repaint();
}
/* Method: add(comp) */
/**
* Adds the component to this canvas without changing its location.
* If the component has no size, its size is set to its preferred size.
* The return type is Component
to match the method in
* the Container
class, but the result is typically
* ignored.
*
* Example: gc.add(comp);
* @param comp The component to add
*/
public Component add(Component comp) {
super.add(comp);
Dimension size = comp.getSize();
if (size.width == 0 || size.height == 0) {
Dimension preferredSize = comp.getPreferredSize();
if (size.width == 0) size.width = preferredSize.width;
if (size.height == 0) size.height = preferredSize.height;
comp.setSize(size);
}
return comp;
}
/* Method: add(comp, x, y) */
/**
* Adds the component to this canvas and sets its location
* to the point (x
, y
).
*
* Example: gc.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));
add(comp);
}
/* Method: add(comp, pt) */
/**
* Adds the component to this canvas and sets its location to the specified point.
*
* Example: gc.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(comp) */
/**
* Removes the component from the canvas.
*
* Example: gc.remove(comp);
* @param comp The component to remove
*/
public void remove(Component comp) {
super.remove(comp);
conditionalRepaint();
}
/* Method: getElementCount() */
/**
* Returns the number of graphical objects stored in this GCanvas
.
*
* Example: int n = gc.getElementCount();
* @return The number of graphical objects in this GCanvas
*/
public int getElementCount() {
return contents.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 = gc.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 contents.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 = gc.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 contents.getElementAt(x, y, false);
}
/* Method: getElementAt(pt) */
/**
* Returns the topmost graphical object that contains the specified point,
* or null
if no such object exists.
*
* Example: GObject gobj = gc.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.
*
* Example: Iterator i = gc.iterator();
* @return An Iterator
ranging over the elements of the
* container from back to front
*/
public Iterator iterator() {
return new GIterator(this, GContainer.BACK_TO_FRONT);
}
/* 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 GContainer.FRONT_TO_BACK
* or GContainer.BACK_TO_FRONT
.
*
* for (Iterator<GObject> i = gc.iterator(direction); i.hasNext(); )
*
* Example: Iterator i = gc.iterator(direction);
* @param direction FRONT_TO_BACK or BACK_TO_FRONT
* @return An Iterator
ranging over the elements of the
* container in the specified direction
*/
public Iterator iterator(int direction) {
return new GIterator(this, direction);
}
/* Method: setOpaque(flag) */
/**
* Sets a flag indicating whether this canvas is opaque, which means that it
* obscures anything behind it. Setting this flag to false
makes
* the GCanvas
transparent, so that any other lightweight components
* behind it show through.
*
* Example: gc.setOpaque(flag);
* @param flag true
to make this canvas opaque, and false
* to make it transparent
*
*/
public void setOpaque(boolean flag) {
opaque = flag;
conditionalRepaint();
}
/* Method: isOpaque() */
/**
* Returns a boolean value indicating whether this canvas is opaque.
*
* Example: if (gc.isOpaque(flag)) . . .
* @return true
if this canvas is opaque, and false
* if it is transparent
*
*/
public boolean isOpaque() {
return opaque;
}
/* Method: getWidth() */
/**
* Returns the width of this canvas in pixels. This method is defined in JDK 1.2
* components, but not in earlier ones. Defining this method here makes this entry
* available in all browsers.
*
* Example: int width = gc.getWidth();
* @return The width of this canvas
*/
public int getWidth() {
return getSize().width;
}
/* Method: getHeight() */
/**
* Returns the height of this canvas in pixels. This method is defined in JDK 1.2
* components, but not in earlier ones. Defining this method here makes this entry
* available in all browsers.
*
* Example: int height = gc.getHeight();
* @return The height of this canvas in pixels
*/
public int getHeight() {
return getSize().height;
}
/* Method: paint(g) */
/**
* Paints the canvas. This method is not ordinarily called by clients.
*
* Example: gc.paint(g);
* @param g The graphics context into which the canvas is painted
*
*/
public void paint(Graphics g) {
Graphics g0 = g;
if (isOpaque()) {
if (offscreenImage == null) initOffscreenImage();
if (offscreenImage != null) g = offscreenImage.getGraphics();
Dimension size = getSize();
g.setColor(getBackground());
g.fillRect(0, 0, size.width, size.height);
g.setColor(getForeground());
}
contents.mapPaint(g);
if (isOpaque() && offscreenImage != null) {
g0.drawImage(offscreenImage, 0, 0, this);
}
super.paint(g0);
}
/* Method: update(g) */
/**
* Updates the canvas. This method is overridden here to support transparency
* in the Swing style.
*
* Example: gc.update(g);
* @param g The graphics context into which the canvas is painted
*
*/
public void update(Graphics g) {
paint(g);
}
/* Method: setAutoRepaintFlag(state) */
/**
* Changes the setting of the auto-repaint flag. By default, any change to a
* graphical object contained in this canvas automatically triggers a repaint
* of the canvas as a whole. While this behavior makes it much easier to use
* the package, it has the disadvantage that repaint requests come much more
* frequently than necessary. You can disable this feature by calling
* setAutoRepaintFlag(false)
, but you must then make explicit
* calls to repaint()
whenever you want to update the display.
* The advantage of this model is that you can then make many different changes
* and have them all appear at once with a single repaint
call.
*
* Example: gc.setAutoRepaintFlag(state);
* @param state true
to enable auto-repaint mode, and false
* to disable it
*/
public void setAutoRepaintFlag(boolean state) {
autoRepaint = state;
}
/* Method: getAutoRepaintFlag() */
/**
* Returns the current setting of the auto-repaint flag as described in
* setAutoRepaintFlag
.
*
* Example: if (gc.getAutoRepaintFlag()) . . .
* @return true
if auto-repaint mode is enabled, and false
* otherwise
*/
public boolean getAutoRepaintFlag() {
return autoRepaint;
}
/* Method: setNativeArcFlag(state) */
/**
* Sets whether the redering code for GArc
and GOval
should use
* Java arcs. By default, arcs and ovals are rendered using polygons and polylines
* to ensure that the same set of pixels is covered by the fill and frame.
* If this value is set to true
, the renderers will use the native
* arc code, which is more efficient but less accurate.
*
* Example: gc.setNativeArcFlag(state);
* @param state true
to enable native arcs, false
to use polygons
*/
public void setNativeArcFlag(boolean state) {
nativeArcFlag = state;
}
/* Method: getNativeArcFlag() */
/**
* Returns the current setting of the native-arc flag as described in
* setNativeArcFlag
.
*
* Example: if (gc.getNativeArcFlag()) . . .
* @return true
if native arcs are enabled, and false
* otherwise
*/
public boolean getNativeArcFlag() {
return nativeArcFlag;
}
/* Protected method: sendToFront(gobj) */
/**
* Implements the sendToFront
function from the GContainer
* interface. Clients should not be calling this method, but the semantics of
* interfaces forces it to be exported.
*
* @param gobj object to relocate
*/
protected void sendToFront(GObject gobj) {
contents.sendToFront(gobj);
conditionalRepaint();
}
/* Protected method: sendToBack(gobj) */
/**
* Implements the sendToBack
function from the GContainer
* interface. Clients should not be calling this method, but the semantics of
* interfaces forces it to be exported.
*
* @param gobj object to relocate
*/
protected void sendToBack(GObject gobj) {
contents.sendToBack(gobj);
conditionalRepaint();
}
/* Protected method: sendForward(gobj) */
/**
* Implements the sendForward
function from the GContainer
* interface. Clients should not be calling this method, but the semantics of
* interfaces forces it to be exported.
*
* @param gobj object to relocate
*/
protected void sendForward(GObject gobj) {
contents.sendForward(gobj);
conditionalRepaint();
}
/* Protected method: sendBackward(gobj) */
/**
* Implements the sendBackward
function from the GContainer
* interface. Clients should not be calling this method, but the semantics of
* interfaces forces it to be exported.
*
* @param gobj object to relocate
*/
protected void sendBackward(GObject gobj) {
contents.sendBackward(gobj);
conditionalRepaint();
}
/* Protected method: dispatchMouseEvent(e) */
/**
* Dispatches this mouse event to the uppermost graphical object for which
* the active point is within the object bounds.
*
* Example: gc.dispatchMouseEvent(MouseEvent e);
* @param e The event that triggered this response
*
*/
protected void dispatchMouseEvent(MouseEvent e) {
GObject gobj = contents.getElementAt(e.getX(), e.getY(), true);
MouseEvent newEvent = null;
if (gobj != lastObject) {
if (lastObject != null) {
newEvent = new GMouseEvent(lastObject, MouseEvent.MOUSE_EXITED, e);
lastObject.fireMouseListeners(newEvent);
}
if (gobj != null) {
newEvent = new GMouseEvent(gobj, MouseEvent.MOUSE_ENTERED, e);
gobj.fireMouseListeners(newEvent);
}
}
lastObject = gobj;
if (dragObject != null) gobj = dragObject;
if (gobj != null) {
int id = e.getID();
if (id != MouseEvent.MOUSE_EXITED && id != MouseEvent.MOUSE_ENTERED) {
if (id != MouseEvent.MOUSE_DRAGGED || dragObject != null) {
if (id == MouseEvent.MOUSE_PRESSED) {
dragObject = gobj;
} else if (id == MouseEvent.MOUSE_RELEASED) {
dragObject = null;
}
newEvent = new GMouseEvent(gobj, id, e);
gobj.fireMouseListeners(newEvent);
}
}
}
if (newEvent != null && newEvent.isConsumed()) e.consume();
}
/* Protected method: initOffscreenImage() */
/**
* Initializes the offscreen memory image used for double buffering.
*
* Example: gc.initOffscreenImage();
*
*/
protected void initOffscreenImage() {
Dimension size = getSize();
if (size.width <= 0 || size.height <= 0) return;
offscreenImage = createImage(size.width, size.height);
}
/* Protected method: conditionalRepaint() */
/**
* Repaints the canvas if auto-repaint is in effect. This method is called only
* by the repaint
method in
* GObject
and is not accessible outside the package.
*
* Example: gc.conditionalRepaint();
*
*/
protected void conditionalRepaint() {
if (autoRepaint) repaint();
}
/* Protected method: updateEnabledList() */
/**
* Reconstructs the enabledList list in the correct order.
*
* Example: gc.updateEnabledList();
*/
protected void updateEnabledList() {
contents.updateEnabledList();
}
/* Package-private static method: createMouseEvent(gobj, eventID, e) */
/**
* Creates a new GMouseEvent
object with gobj
* effective source and eventID
; all other fields are
* copied from the event e
. This method must be included
* in this class to avoid cross-file references to GMouseEvent from
* the GCompound class.
*/
static MouseEvent createMouseEvent(Object gobj, int eventID, MouseEvent e) {
return new GMouseEvent(gobj, eventID, e);
}
/* Private instance variables */
private GCanvasListener gCanvasListener;
private GObject lastObject;
private GObject dragObject;
private GObjectList contents;
private Image offscreenImage;
private boolean autoRepaint;
private boolean nativeArcFlag;
private boolean opaque;
}
/* Package class: GCanvasListener */
/**
* This class fields mouse events that occur in the GCanvas
.
*/
class GCanvasListener
implements MouseListener, MouseMotionListener, ComponentListener {
/* Constructor: GCanvasListener(gc) */
/**
* Creates a new listener object that watches for mouse events in the
* GCanvas
.
*
* Example: GCanvasListener listener = new GCanvasListener(gc);
* @param gc The GCanvas
object to which this listens
*/
public GCanvasListener(GCanvas gc) {
gCanvas = gc;
}
/* MouseListener interface */
public void mouseClicked(MouseEvent e) { gCanvas.dispatchMouseEvent(e); }
public void mousePressed(MouseEvent e) { gCanvas.requestFocus(); gCanvas.dispatchMouseEvent(e); }
public void mouseReleased(MouseEvent e) { gCanvas.dispatchMouseEvent(e); }
public void mouseEntered(MouseEvent e) { gCanvas.dispatchMouseEvent(e); }
public void mouseExited(MouseEvent e) { gCanvas.dispatchMouseEvent(e); }
/* MouseMotionListener interface */
public void mouseDragged(MouseEvent e) { gCanvas.dispatchMouseEvent(e); }
public void mouseMoved(MouseEvent e) { gCanvas.dispatchMouseEvent(e); }
/* ComponentListener interface */
public void componentResized(ComponentEvent e) {
gCanvas.initOffscreenImage();
if (gCanvas.isShowing()) gCanvas.repaint();
}
public void componentHidden(ComponentEvent e) { }
public void componentMoved(ComponentEvent e) { }
public void componentShown(ComponentEvent e) { }
/* Private instance variables */
private GCanvas gCanvas;
}
class GMouseEvent extends MouseEvent {
/* Constructor: GMouseEvent(gobj, eventID, e) */
/**
* Creates a new GMouseEvent
object with gobj
* effective source and eventID
; all other fields are
* copied from the event e
.
*/
public GMouseEvent(Object gobj, int eventID, MouseEvent e) {
super(e.getComponent(), eventID, e.getWhen(), e.getModifiers(),
e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger());
effectiveSource = gobj;
}
/* Method: getSource() */
/**
* Overrides getSource
to return the effective source of this event,
* which will typically be a GObject
rather than the GCanvas
* that triggered the event.
*/
public Object getSource() {
return effectiveSource;
}
/* Method: getComponent() */
/**
* Overrides getComponent
to return the GCanvas
* that triggered the event.
*/
public Component getComponent() {
return (Component) super.getSource();
}
/* Method: toString() */
/**
* Overrides toString
to display the correct source for this event.
*/
public String toString() {
return getClass().getName() + "[" + paramString() + "] on " + getSource();
}
/* Private instance variables */
private Object effectiveSource;
}