diva.canvas
Class FigureLayer

java.lang.Object
  extended by diva.canvas.CanvasLayer
      extended by diva.canvas.FigureLayer
All Implemented Interfaces:
CanvasComponent, EventAcceptor, FigureContainer, FigureSet, VisibleComponent

public class FigureLayer
extends CanvasLayer
implements FigureContainer, EventAcceptor

A figure layer is a layer on which Figures can be drawn. It contains a z-list in which all the contained figures are held, and implements wrappers for most of the z-list methods to provide flexible access to the contained figures. Figures are also stored at contiguous integer indexes, with index zero being the "topmost" figure and the highest index being the "lowest" figure.

FigureLayer responds to events on figures themselves by forwarding the events to interactors attached to a hit figure. It does not support events on the layer itself (see EventLayer for that).

Version:
$Id: FigureLayer.java 38798 2005-07-08 20:00:01Z cxh $
Author:
John Reekie
Accepted Rating:
Yellow

Field Summary
private  boolean _enabled
          The enabled flag.
private  LayerEvent _lastLayerEvent
          The last layer event processed by this layer.
private  double _pickHalo
          The size of the halo to use for "picking."
private  Figure _pointerGrabber
          The figure that has currently grabbed the pointer.
private  Figure _pointerOver
          The figure that the pointer is currently over.
private  boolean _visible
          The visibility flag.
private  ZList _zlist
          The figures contained in this layer.
 
Fields inherited from class diva.canvas.CanvasLayer
_containingPane
 
Constructor Summary
FigureLayer()
          Create a new figure layer that is not in a pane.
FigureLayer(CanvasPane pane)
          Create a new figure layer within the given pane.
FigureLayer(CanvasPane pane, ZList zlist)
          Create a new figure layer within the given pane and with the given ZList to hold the figures it contains.
FigureLayer(ZList zlist)
          Create a new figure layer with the given ZList to hold its figures.
 
Method Summary
 void add(Figure f)
          Add a figure to the layer.
 void add(int index, Figure f)
          Insert a figure into the layer at the given position.
 void clear()
          Removes all of the figures from this layer.
 boolean contains(Figure f)
          Test if the layer contains the given figure.
 void decorate(Figure child, FigureDecorator decorator)
          Decorate a child figure, replacing the child figure with the decorator.
 void dispatchEvent(java.awt.AWTEvent event)
          Dispatch an AWT event on this layer.
private  void dispatchEventUpTree(Figure f, LayerEvent e)
          Dispatch a layer event up the tree.
private  void dispatchMotionEventUpTree(Figure f, LayerEvent e)
          Dispatch a layer motion event up the tree.
 java.util.Iterator figures()
          Return an iteration of the figures in this container.
 java.util.Iterator figuresFromBack()
          Return an iteration of the figures in this container, from back to front.
 java.util.Iterator figuresFromFront()
          Return an iteration of the figures in this container, from front to back.
 Figure get(int index)
          Get the figure at the given index.
 Figure getCurrentFigure()
          Return the figure that the mouse pointer is currently over, or null if none.
protected  Figure getFigure(LayerEvent e)
          Return the figure pointed to by the given LayerEvent.
 int getFigureCount()
          Return the number of figures in this layer.
 ZList getFigures()
          Get the internal z-list.
 java.awt.geom.Rectangle2D getLayerBounds()
          Get the bounds of the shapes draw in this layer.
 double getPickHalo()
          Get the "pick halo".
 java.lang.String getToolTipText(LayerEvent e)
          Get the toolTipText for the point in the given LayerEvent.
 void grabPointer(LayerEvent e, Figure f)
          "Grab" the pointer.
 int indexOf(Figure f)
          Return the index of the given figure.
 boolean isEnabled()
          Test the enabled flag of this layer.
 boolean isVisible()
          Test the visibility flag of this layer.
 void paint(java.awt.Graphics2D g)
          Paint this layer onto a 2D graphics object.
 void paint(java.awt.Graphics2D g, java.awt.geom.Rectangle2D region)
          Paint this layer onto a 2D graphics object, within the given region.
 Figure pick(java.awt.geom.Rectangle2D region)
          Get the picked figure.
 Figure pick(java.awt.geom.Rectangle2D region, Filter filter)
          Get the picked figure.
protected  void processLayerEvent(LayerEvent e)
          Process a layer event.
protected  void processLayerMotionEvent(LayerEvent e)
          Process a layer motion event.
 void remove(Figure f)
          Remove the given figure from this layer.
 void remove(int index)
          Remove the figure at the given position in the list.
 void repaint(java.awt.geom.Rectangle2D region)
          Repaint all figures that intersect the given rectangle.
 void setEnabled(boolean flag)
          Set the enabled flag of this layer.
 void setIndex(int index, Figure f)
          Set the index of the given figure.
 void setPickHalo(double halo)
          Set the "pick halo".
 void setVisible(boolean flag)
          Set the visibility flag of this layer.
 void undecorate(FigureDecorator decorator)
          Remove a figure from the given decorator and add it back into this container.
 
Methods inherited from class diva.canvas.CanvasLayer
getCanvasPane, getParent, getTransformContext, repaint, repaint, setParent
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 
Methods inherited from interface diva.canvas.CanvasComponent
getParent, getTransformContext, repaint, repaint
 

Field Detail

_zlist

private ZList _zlist
The figures contained in this layer.


_pickHalo

private double _pickHalo
The size of the halo to use for "picking."


_visible

private boolean _visible
The visibility flag.


_enabled

private boolean _enabled
The enabled flag.


_pointerOver

private Figure _pointerOver
The figure that the pointer is currently over.


_pointerGrabber

private Figure _pointerGrabber
The figure that has currently grabbed the pointer.


_lastLayerEvent

private LayerEvent _lastLayerEvent
The last layer event processed by this layer.

Constructor Detail

FigureLayer

public FigureLayer()
Create a new figure layer that is not in a pane. The layer will not be displayed, and its coordinate transformation will be as though it were a one-to-one mapping. Use of this constructor is strongly discouraged, as many of the geometry-related methods expect to see a pane.


FigureLayer

public FigureLayer(CanvasPane pane)
Create a new figure layer within the given pane.


FigureLayer

public FigureLayer(CanvasPane pane,
                   ZList zlist)
Create a new figure layer within the given pane and with the given ZList to hold the figures it contains.


FigureLayer

public FigureLayer(ZList zlist)
Create a new figure layer with the given ZList to hold its figures. This can be used to create a more efficient z-list than the default, which is an instance of BasicZList.

Method Detail

add

public void add(Figure f)
Add a figure to the layer. The figure is added to the ZList, and the figure's layer and parent fields set appropriately. The figure will be painted over the top of all existing figures. It does not check whether the figure is already in the layer -- clients clients are therefore responsible for being bug-free.

Specified by:
add in interface FigureContainer

add

public void add(int index,
                Figure f)
Insert a figure into the layer at the given position. To insert the figure just in front of some other figure, use getIndex() to get the other figure's index, and pass index as the first argument. To insert the figure just behind some other figure, pass index+1 as the first argument. To insert so the figure displays over the top of other figures, insert at zero.

Clients should assume that an implementation of this method does not check if the figure is already contained -- clients are therefore responsible for being bug-free.


clear

public void clear()
Removes all of the figures from this layer.


contains

public boolean contains(Figure f)
Test if the layer contains the given figure. Note that, in general, a much better way of making this same test is to check if the parent of the figure is the same object as this layer.

Specified by:
contains in interface FigureContainer
Specified by:
contains in interface FigureSet

decorate

public void decorate(Figure child,
                     FigureDecorator decorator)
Decorate a child figure, replacing the child figure with the decorator.

Specified by:
decorate in interface FigureContainer

dispatchEvent

public void dispatchEvent(java.awt.AWTEvent event)
Dispatch an AWT event on this layer. If the layer is not enabled, return immediately. Otherwise process the event according to its type. If the event represents a mouse click, drag, or release, call the protected method processLayerEvent; if it represents a mouse movement, call the protected method processLayerMotionEvent. Currently other events types are not handled.

Specified by:
dispatchEvent in interface EventAcceptor

figures

public java.util.Iterator figures()
Return an iteration of the figures in this container. The order in which figures are iterated is undefined.

Specified by:
figures in interface FigureSet

figuresFromBack

public java.util.Iterator figuresFromBack()
Return an iteration of the figures in this container, from back to front. This is the order in which figures should normally be painted.

Specified by:
figuresFromBack in interface FigureSet

figuresFromFront

public java.util.Iterator figuresFromFront()
Return an iteration of the figures in this container, from front to back. This is the order in which events should normally be intercepted.

Specified by:
figuresFromFront in interface FigureSet

get

public Figure get(int index)
Get the figure at the given index. Indexes are contiguous from zero to getFigureCount()-1, with figures at lower indexes being displayed "on top of" figures with higher indexes.


getFigureCount

public int getFigureCount()
Return the number of figures in this layer.

Specified by:
getFigureCount in interface FigureContainer

getCurrentFigure

public Figure getCurrentFigure()
Return the figure that the mouse pointer is currently over, or null if none.

Returns:
The figure the mouse is currently over.

getFigures

public ZList getFigures()
Get the internal z-list. Clients must not modify the z-list, but can use it for making queries on its contents.


getLayerBounds

public java.awt.geom.Rectangle2D getLayerBounds()
Get the bounds of the shapes draw in this layer. In this class, we return the bounds of all the figures in the z-list.

Overrides:
getLayerBounds in class CanvasLayer

getPickHalo

public final double getPickHalo()
Get the "pick halo". This is the distance in either axis that an object can be from the mouse to be considered hit.


grabPointer

public void grabPointer(LayerEvent e,
                        Figure f)
"Grab" the pointer. Typically, this method will be called from an Interactor class in response to a mousePressed() event. The effect of calling this method is to make the series of mouse events up to and including the mouseReleased() event appear to be originating from the figure f rather than the actual figure that was clicked on. For example, if clicking and dragging on a graph node creates and the drags a new edge, this method will be called after constructing the edge to make the edge itself handle the mouse drag events instead of the node.


getToolTipText

public java.lang.String getToolTipText(LayerEvent e)
Get the toolTipText for the point in the given LayerEvent. This method starts with the figure that is set by the mouse motion events when the pointer moves over a figure. Starting with that figure, it walks up the tree until it finds a figure that returns a tool tip, or until it reaches a root figure. If it finds a tool tip, it returns it, otherwise it returns null.

Overrides:
getToolTipText in class CanvasLayer

indexOf

public int indexOf(Figure f)
Return the index of the given figure. Figures with a higher index are drawn behind figures with a lower index.


isEnabled

public final boolean isEnabled()
Test the enabled flag of this layer. Note that this flag does not indicate whether the layer is actually enabled, as its pane or one if its ancestors may not be enabled.

Specified by:
isEnabled in interface EventAcceptor

isVisible

public final boolean isVisible()
Test the visibility flag of this layer. Note that this flag does not indicate whether the layer is actually visible on the screen, as its pane or one if its ancestors may not be visible.

Specified by:
isVisible in interface VisibleComponent

paint

public void paint(java.awt.Graphics2D g)
Paint this layer onto a 2D graphics object. If the layer is not visible, return immediately. Otherwise paint all figures from back to front.

Specified by:
paint in interface VisibleComponent

paint

public void paint(java.awt.Graphics2D g,
                  java.awt.geom.Rectangle2D region)
Paint this layer onto a 2D graphics object, within the given region. If the layer is not visible, return immediately. Otherwise paint all figures that overlap the given region, from back to front.

Specified by:
paint in interface VisibleComponent

pick

public Figure pick(java.awt.geom.Rectangle2D region)
Get the picked figure. This method recursively traverses the tree until it finds a figure that is "hit" by the region. Note that a region is given instead of a point so that "hysteresis" can be implemented. If no figure is picked, return null. The region should not have zero size, or no figure will be hit.

Specified by:
pick in interface FigureContainer

pick

public Figure pick(java.awt.geom.Rectangle2D region,
                   Filter filter)
Get the picked figure. This method recursively traverses the tree until it finds a figure that is "hit" by the region. Note that a region is given instead of a point so that "hysteresis" can be implemented. If no figure is picked, return null. The region should not have zero size, or no figure will be hit.

Specified by:
pick in interface FigureContainer

remove

public void remove(Figure f)
Remove the given figure from this layer. The figure's layer is set to null.

Specified by:
remove in interface FigureContainer

remove

public void remove(int index)
Remove the figure at the given position in the list. The figure's layer is set to null.


repaint

public void repaint(java.awt.geom.Rectangle2D region)
Repaint all figures that intersect the given rectangle.


setEnabled

public final void setEnabled(boolean flag)
Set the enabled flag of this layer. If the flag is false, then the layer will not respond to user input events.

Specified by:
setEnabled in interface EventAcceptor

setPickHalo

public final void setPickHalo(double halo)
Set the "pick halo". This is the distance a figure can be from the mouse in either axis to be considered hit by the mouse. By default, it it set to 0.5, meaning that the hit detection rectangle is 1.0 along each side.


setIndex

public void setIndex(int index,
                     Figure f)
Set the index of the given figure. That is, move it in the display list to the given position. To move the figure to just in front of some other figure, use getIndex() to get the other figure's index, and pass index as the first argument. To move the figure to just behind some other figure, pass index+1 as the first argument. (Other figures will have their indexes changed accordingly.)

Note that this method does not check if the figure is already contained -- clients are therefore responsible for being bug-free.

Throws:
java.lang.IndexOutOfBoundsException - The new index is out of range.

setVisible

public final void setVisible(boolean flag)
Set the visibility flag of this layer. If the flag is false, then the layer will not be drawn on the screen.

Specified by:
setVisible in interface VisibleComponent

undecorate

public void undecorate(FigureDecorator decorator)
Remove a figure from the given decorator and add it back into this container.

Specified by:
undecorate in interface FigureContainer

dispatchEventUpTree

private void dispatchEventUpTree(Figure f,
                                 LayerEvent e)
Dispatch a layer event up the tree. Proceed up the hierarchy looking for a figure that has an interactor that is enabled for layer events. Dispatch the event to the first one found. If the event is not consumed, repeat.


dispatchMotionEventUpTree

private void dispatchMotionEventUpTree(Figure f,
                                       LayerEvent e)
Dispatch a layer motion event up the tree. Proceed up the hierarchy looking for a figure that has an interactor that is enabled for motion events. Dispatch the event to the first one found. The event does not propagate up the tree any further, regardless of whether the event was consumed or not. (Is this the right behavior??)


getFigure

protected final Figure getFigure(LayerEvent e)
Return the figure pointed to by the given LayerEvent. If there is no figure, then return null.


processLayerEvent

protected void processLayerEvent(LayerEvent e)
Process a layer event. The behaviour of this method depends on the action type. If it is MOUSE_PRESSED, then it recurses down the figure tree searching for the top-most figure under the cursor. (If a figure has been hit on its transparent part, then it will not be considered to be above another figure -- the method Figure.hits() determines whether a figure gets the event.) When it finds it, it remembers it, and then proceeds back up the tree, passing the event to the event dispatcher of any figures that have one. After each such call, if the event has been consumed, then the upwards-traversal stops. Finally, if this layer is reached, and the event has not been consumed, then any registered LayerListeners are called. (Or should they be notified in any case?)

If the event type is MOUSE_DRAGGED or MOUSE_RELEASED, then the downwards recursion is skipped, and the upwards propagation is begun from the figure remembered from the MOUSE_PRESSED processing. Again, the propagation stops when the event is consumed.

Note: the above strategy will not work with more than one input device. Is there anything in MouseEvent that allows us to identify the input device?


processLayerMotionEvent

protected void processLayerMotionEvent(LayerEvent e)
Process a layer motion event. The behavior of this method depends on the action type. If the action is MOUSE_ENTERED. then the figure tree is scanned to find if the mouse is now over a figure, and the event is dispatched to that figure if so. If the action is MOUSE_MOVED, then the tree is scanned again to find the figure currently under the mouse; if it is different, then leave and enter events are generated on the previous and current figures (either may not exist); otherwise a motion event is generated on the current figure. If the action is MOUSE_EXITED, then the current figure, if there is one, has an exit event sent to it. In all of these cases the event is propagated from the current figure up the hierarchy until consumed.