com.hdcookbook.grin
Class Show

java.lang.Object
  extended by com.hdcookbook.grin.Show
All Implemented Interfaces:
AnimationClient

public class Show
extends java.lang.Object
implements AnimationClient

Represents a show. A show is the top-level node in a scene graph. It is composed of a number of segments. A show progresses by moving through segments; a show has exactly one active segment while it is running. A segment is composed of a number of visual features, plus a set of remote control handlers.

Author:
Bill Foote (http://jovial.com)

Field Summary
 java.awt.Component component
          The component we're presented within.
protected  Feature[] features
           
protected  java.lang.String[] fontName
           
protected  int[] fontStyleSize
           
 ShowInitializer initializer
          An object used to hold state during initializaition of a show.
protected  java.util.Hashtable publicFeatures
           
protected  java.util.Hashtable publicNamedCommands
           
protected  java.util.Hashtable publicRCHandlers
           
protected  java.util.Hashtable publicSegments
           
protected  RCHandler[] rcHandlers
           
protected  Segment[] segments
           
 SetupManager setupManager
          Our helper that calls into us to load images and such.
protected  Segment showTop
          This is a dummy segment which contains one feature in its activated feature array, which represents the top of the rendering tree.
protected  Group showTopGroup
          A group representing a set of activated features in the currently activated Segment.
protected  ManagedImage[] stickyImages
          This is the set of images that are "sticky".
 
Constructor Summary
Show(Director director)
          Create a new show.
 
Method Summary
 void activateSegment(Segment seg)
          Set the current segment.
 void activateSegment(Segment seg, boolean push)
          Set the current segment.
 void addDisplayAreas(RenderContext context)
          Tell the animation manager what areas need to be drawn to for the next frame.
 void buildShow(Segment[] segments, Feature[] features, RCHandler[] rcHandlers, java.lang.String[] stickyImages, Segment showTop, Group showTopGroup, java.util.Hashtable publicSegments, java.util.Hashtable publicFeatures, java.util.Hashtable publicRCHandlers, java.util.Hashtable publicNamedCommands, java.lang.String[] fontName, int[] fontStyleSize)
          This is called to build the show.
 void deferNextCommands()
          This method, which should ONLY be called from the execute() method of a command, suspends processing of further queued commands until the display of the show has caught up with the screen.
 void destroy()
          Destroy this animatin client.
 void doActivateSegment(Segment newS)
          This is called from ActivateSegmentCommand, and should not be called from anywhere else.
 void doSegmentDone()
          This is called from GrinXHelper(SEGMENT_DONE), and should not be called from anywhere else.
 Segment getCurrentSegment()
          Get the current segment.
 Director getDirector()
          Get this show's director.
 java.lang.String[] getDrawTargets()
          Get the set of draw target names.
 Feature getFeature(java.lang.String name)
          Look up the given public feature.
 java.awt.Font getFont(int index)
          Get one of the fonts recorded for this show.
 Command getNamedCommand(java.lang.String name)
          Look up the given public named command or command list.
 RCHandler getRCHandler(java.lang.String name)
          Get a public RC handler by name.
 Segment getSegment(java.lang.String name)
          Look up a public segment.
 int getSegmentStackDepth()
          Get the depth of the segment stack.
 int getXOffset()
          Get the x offset that this show was built with.
 int getXScale()
          Get the x scale factor, in mills.
 int getYOffset()
          Get the y offset that this show was built with.
 int getYScale()
          Get the y scale factor, in mills.
 boolean handleKeyPressed(int vkCode)
          Called by the xlet when a key press is received.
 boolean handleKeyReleased(int vkCode)
          Called by the xlet when a key release is received.
 boolean handleKeyTyped(RCKeyEvent typed)
          Called by the xlet when a key typed event is received, or generated (e.g.
 void handleMouseClicked(int x, int y)
          Called by the xlet when the mouse is clicked.
 void handleMouseMoved(int x, int y)
          Called by the xlet when the mouse moves.
 void initialize(java.awt.Component component)
          Initialize the animation client.
 void internalHandleKeyPressed(RCKeyEvent re, Show caller)
          This is an implementation method, called by RCKeyEvent
 void internalHandleKeyReleased(RCKeyEvent re, Show caller)
          This is an implementation method, called by RCKeyEvent
 void internalHandleKeyTyped(RCKeyEvent re, Show caller)
          This method is to be called by a subclass of RCKeyEvent that is created by someone extending GRIN to support key typed events.
 void mapDrawTargets(java.util.Hashtable targetMap)
          Map draw target names into the numeric values that are required by the animation engine.
 void nextFrame()
          Advance the state of the show to the next frame.
 void paintDone()
          Called when the animation framework is done painting the current frame.
 void paintFrame(java.awt.Graphics2D gr)
          Paint the current frame of the animation.
 Segment popSegmentStack()
          Used by the activate segment command.
 void pushCurrentSegment()
          Used by the activate segment command.
 void runCommand(Command cmd)
          Run the given command when we advance to the next frame.
 void runCommands(Command[] cmds)
          Run the given commands when we advance to the next frame.
static int scale(int value, int mills)
          Scale a value by a scale factor in mills.
 void segmentDone()
          Signal to the show that the current segment is done.
 void setCaughtUp()
          Indicate to the animation client that we're not behind in the animation, so that the current frame will actually be displayed.
 void setDrawTargets(java.lang.String[] drawTargets)
          Used to build the show, or to reinitialize it.
 void setScale(int xScale, int yScale, int xOffset, int yOffset)
          Sets the scale and offset values for a show.
 void setSegmentStackDepth(int depth)
          Used to build the show.
 void syncDisplay()
          Synchronize the display to the current show state.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

setupManager

public SetupManager setupManager
Our helper that calls into us to load images and such. This is for internal use only, and is public so that GRIN features in other packages can access it efficiently.


component

public java.awt.Component component
The component we're presented within. This can be needed for things like loading images via Component.prepareImage(). It should only be used on an initialized, non-destroyed show.


initializer

public ShowInitializer initializer
An object used to hold state during initializaition of a show. This is nulled out after the show is initialized. This is for internal use only, and is public so that GRIN classes in other packages can access it efficiently.


segments

protected Segment[] segments

features

protected Feature[] features

rcHandlers

protected RCHandler[] rcHandlers

publicSegments

protected java.util.Hashtable publicSegments

publicFeatures

protected java.util.Hashtable publicFeatures

publicRCHandlers

protected java.util.Hashtable publicRCHandlers

publicNamedCommands

protected java.util.Hashtable publicNamedCommands

stickyImages

protected ManagedImage[] stickyImages
This is the set of images that are "sticky". Sticky images get loaded as normal (that is, by a feature that uses the image being included in the setup clause of a segment), but they are never unloaded. Thus, features that use sticky images will be prepared instantly if subsequently re-used.

This array may be null.


showTop

protected Segment showTop
This is a dummy segment which contains one feature in its activated feature array, which represents the top of the rendering tree.


showTopGroup

protected Group showTopGroup
A group representing a set of activated features in the currently activated Segment. This data member is accessed from Segment.activate(Segment) and Segment.runFeatureSetup().


fontName

protected java.lang.String[] fontName

fontStyleSize

protected int[] fontStyleSize
Constructor Detail

Show

public Show(Director director)
Create a new show.

Parameters:
director - A Director helper class the xlet can use to control the show. May be null, in which case a default direct instance of Director will be assigned.
Method Detail

getDirector

public Director getDirector()
Get this show's director. If the show was created without a director, a default one is created, and this will be returned.

Returns:
our Director

buildShow

public void buildShow(Segment[] segments,
                      Feature[] features,
                      RCHandler[] rcHandlers,
                      java.lang.String[] stickyImages,
                      Segment showTop,
                      Group showTopGroup,
                      java.util.Hashtable publicSegments,
                      java.util.Hashtable publicFeatures,
                      java.util.Hashtable publicRCHandlers,
                      java.util.Hashtable publicNamedCommands,
                      java.lang.String[] fontName,
                      int[] fontStyleSize)
               throws java.io.IOException
This is called to build the show. This needs to be done before initialize is called. Normally, clients of the GRIN framework shouldn't call this.

Throws:
java.io.IOException - if anything goes wrong.

setScale

public void setScale(int xScale,
                     int yScale,
                     int xOffset,
                     int yOffset)
Sets the scale and offset values for a show.


getFont

public java.awt.Font getFont(int index)
Get one of the fonts recorded for this show. This is an internal method for use by the text feature, or by other extension features that use show fonts. It should only be called from the setup thread, or from the animation thread after setup.


initialize

public void initialize(java.awt.Component component)
Initialize the animation client. This is called from the animation worker thread before starting the animation loop.

This will be called by the animation framework; clients of the GRIN framework should ensure a show has been built before handing it off to the animation framework.

Specified by:
initialize in interface AnimationClient
Parameters:
component - The component this show will eventually be displayed in. It's used for things like Component.prepareImage().

destroy

public void destroy()
Destroy this animatin client. This is called from the animation worker thread before ending the animation loop.

Destroy a show. This will be called by the animation framework when the animation engine is destroyed, or when this show is removed from the engine's list of shows.

Specified by:
destroy in interface AnimationClient

setSegmentStackDepth

public void setSegmentStackDepth(int depth)
Used to build the show. Clients of the GRIN framework should not call this method directly.


setDrawTargets

public void setDrawTargets(java.lang.String[] drawTargets)
Used to build the show, or to reinitialize it.


getDrawTargets

public java.lang.String[] getDrawTargets()
Get the set of draw target names. The numerical draw targets in features (e.g. the SetTarget feature) don't necessarily correspond to indicies in this array, because when AnimationClient instances are set up in an AnimationEngine, a master list of all of the unique draw targets is built.

Specified by:
getDrawTargets in interface AnimationClient
See Also:
RenderContext.setTarget(int), mapDrawTargets(Hashtable), SetTarget

mapDrawTargets

public void mapDrawTargets(java.util.Hashtable targetMap)
Map draw target names into the numeric values that are required by the animation engine. This is called during initialization. By calling this on all AnimationClient instances, clients can share draw targets by giving them the same name.

The total number of different draw targets in an animation should be small, because the algorithm for combining draw targets is of cubic time complexity. One or two different draw targets might be a typical number, and more than four is questionable.

See addDisplayAreas() for a discussion of what a render area target is for.

Specified by:
mapDrawTargets in interface AnimationClient
Parameters:
targetMap - A hashtable mapping String names to Integer values
See Also:
RenderContext.setTarget(int), AnimationClient.addDisplayAreas(RenderContext)

getFeature

public Feature getFeature(java.lang.String name)
Look up the given public feature.

Returns:
feature, or null if not found

getNamedCommand

public Command getNamedCommand(java.lang.String name)
Look up the given public named command or command list. This Command object can be used in a call to runCommand(Command) on the show from whene it came. If you send a named command to a different show, the results are undefined.

Returns:
the Command, or null

getRCHandler

public RCHandler getRCHandler(java.lang.String name)
Get a public RC handler by name.

Returns:
rc handler, or null if not found

getSegment

public Segment getSegment(java.lang.String name)
Look up a public segment. This is done without taking out the show lock. It's OK to call this before the show is initialized.

Returns:
segment, or null if not found.

getSegmentStackDepth

public int getSegmentStackDepth()
Get the depth of the segment stack. This is how many times you can push a segment without an old value falling off the end of the stack. It's set in the show file.


activateSegment

public void activateSegment(Segment seg)
Set the current segment. This is the main way an application controls what is being displayed on the screen. The new segment will become current when we advance to the next frame.

This can be called from any thread; it does not take out the show lock or any other global locks. If the show has been destroyed, calling this method has no effect. It's OK to call this before the show has been initialized.

The current segment is not pushed onto the segment activation stack.

Parameters:
seg - The segment to activate, or null to pop the segment activation stack;

activateSegment

public void activateSegment(Segment seg,
                            boolean push)
Set the current segment. This is the main way an application controls what is being displayed on the screen. The new segment will become current when we advance to the next frame.

This can be called from any thread; it does not take out the show lock or any other global locks. If the show has been destroyed, calling this method has no effect.

Parameters:
seg - The segment to activate, or null to pop the segment activation stack.
push - When true and when the segment is non-null, the current segment will be pushed onto the segment activation stack as the show transitions to the new segment.

pushCurrentSegment

public void pushCurrentSegment()
Used by the activate segment command. Clients of the GRIN framework should never call this method directly.


popSegmentStack

public Segment popSegmentStack()
Used by the activate segment command. Clients of the GRIN framework should never call this method directly.


syncDisplay

public void syncDisplay()
Synchronize the display to the current show state. This works by queueing a show command. When executed, this command will prevent the execution of any other show commands, until the display has caught up to the internal program state. This is a good thing to do before a command that is potentially time-consuming, like selecting a playlist. To flesh out this example, on some players, while a playlist is being selected, the Java runtime does not get a chance to run and update the screen, so it's good to make sure the screen is up-to-date before starting the playlist selection.

Note carefully the difference between syncDisplay() and deferNextCommands(). If you're about to queue a command that does something time-consuming, then you should call syncDisplay() first, so the display is guaranteed to be updated. If, however, you're executing within the body of the command and you want to make sure the display gets synchronized before the next command in the queue (which was already there), then you want to call deferNextCommands().

This is equivalent to the sync_display GRIN command.

See Also:
deferNextCommands()

segmentDone

public void segmentDone()
Signal to the show that the current segment is done. This causes the segment to execute its "next" command block, which in most shows should contain a command to move to a different segment.

This is equivalent to the segment_done GRIN command.


runCommand

public void runCommand(Command cmd)
Run the given command when we advance to the next frame. If the show has been destroyed, this has no effect.

This can be called from any thread; it does not take out the show lock or any other global locks. If the show has been destroyed, calling this method has no effect. It's OK to call this before the show has been initialized.

See Also:
runCommands(Command[])

runCommands

public void runCommands(Command[] cmds)
Run the given commands when we advance to the next frame. If the show has been destroyed, this has no effect.

This can be called from any thread; it does not take out the show lock or any other global locks. If the show has been destroyed, calling this method has no effect. It's OK to call this before the show has been initialized.

See Also:
runCommand(Command)

setCaughtUp

public void setCaughtUp()
Indicate to the animation client that we're not behind in the animation, so that the current frame will actually be displayed. Clients shouldn't make any changes to the model in this call; all such changes need to happen in nextFrame()

The animation framework calls this method just before calling nextFrame() if the animation loop is caught up. From time to time, pending commands will be deferred until animation has caught up - this is done by the sync_display command. GRIN knows we've caught up when we paint a frame, but calling this method can let it know one frame earlier.

Specified by:
setCaughtUp in interface AnimationClient
See Also:
nextFrame()

nextFrame

public void nextFrame()
               throws java.lang.InterruptedException
Advance the state of the show to the next frame. This is called once per frame; the first time it is called can be considered "frame 0", and can monotonically increase from there.

This method can be called multiple times before any attempt is made to display the UI state. This happens when animation falls behind; the engine catches up by skipping frames. Animation clients should perform only quick updates in this method; any more time-consuming calculations should be deferred until an object is first painted for a given frame.

Specified by:
nextFrame in interface AnimationClient
Throws:
java.lang.InterruptedException - if the show has been destroyed
See Also:
setCaughtUp()

deferNextCommands

public void deferNextCommands()
This method, which should ONLY be called from the execute() method of a command, suspends processing of further queued commands until the display of the show has caught up with the screen. This is done from the sync_display command, but can be done from any other command too. It causes an effect like Toolkit.sync() or the old X-Windows XSync() command, whereby the screen is guaranteed to be up-to-date before further commands are processed.

Doing this can be useful, for example, just before an operation that is time-consuming and CPU-bound on some players, like JMF selection.

See Also:
syncDisplay()

doActivateSegment

public void doActivateSegment(Segment newS)
This is called from ActivateSegmentCommand, and should not be called from anywhere else.


doSegmentDone

public void doSegmentDone()
This is called from GrinXHelper(SEGMENT_DONE), and should not be called from anywhere else.


getCurrentSegment

public Segment getCurrentSegment()
Get the current segment. The caller may wish to synchronize on the show when using this, so that the current segment doesn't change right after the call.


addDisplayAreas

public void addDisplayAreas(RenderContext context)
                     throws java.lang.InterruptedException
Tell the animation manager what areas need to be drawn to for the next frame. This is called just after setCaughtUp(), and just before paintFrame().

In this call, the client must indicate where it intends to draw by calling methods on RenderContext. Internally, a RenderContext keeps a number of rendering area "targets". Each target will keep track of a bounding rectangle of all of drawing operations that are considered within that target. When the call to addDisplayAreas is complete, the animation manager may merge some of these rendering areas targets, or may leave them seperate; it will then call paintFrame() as many times as it needs to, with a different clip rect each time. These clip rects will never overlap, so you don't need to worry about a Src mode drawing to the same pixel twice in the same frame.

The purpose of these targets is to try to minimize the number of pixels that will be updated on the screen in each frame of animation. Consider, for example, the case where most of the screen isn't changing, but where there's a small animation in the upper-left hand corner, and another small animation in the lower-right hand corner. If those two animations used the same target, the overall bounding rectangle would cover the whole screen. By using two different targets, the screen update can be confined to two small rectangles, one at each corner.

The number of render area targets is set up when an AnimationEngine is created, by calling mapDrawTargets() on each client. During animation, each AnimationClient is passed the same set of targets. If there are multiple AnimationClient instances attached to an AnimationEngine, it is up to the programmer to decide which render area targets should be shared between clients so as to optimize drawing performance. This can be done with appropriate naming of the targets in the mapDrawTargets() call.

Often, an AnimationClient only needs to erase or draw objects that have changed. However, under certain circumstances, the AnimationClient will be asked to redraw everything. This will happen on the first frame drawn, and possibly on others. For example, with repaint draw and platform double-buffering, the platform erases the buffer for each frame, so a full redraw is required. When this happens, the animation enginer automatically adds the full extent of the component to one of the targets. In this case, it still calls this method, so that items we draw have the opportunity to erase themselves if needed.

This method will be called exactly once for each frame displayed. Because paintFrame can be called multiple times per frame, any state maintained by the animation client to optimize display areas should be updated in this method, and not in paintFrame().

Specified by:
addDisplayAreas in interface AnimationClient
Parameters:
context - The RenderContext that manages the set of targets the client can draw to.
Throws:
java.lang.InterruptedException - if the thread has been interrupted (e.g. because the xlet is being killed)
See Also:
RenderContext.setTarget(int), AnimationClient.mapDrawTargets(Hashtable)

paintFrame

public void paintFrame(java.awt.Graphics2D gr)
                throws java.lang.InterruptedException
Paint the current frame of the animation. This is called after addDisplayAreas(), as the last step in a cycle through the animation loop. This might be called multiple times for a given frame of animation, with a different clip rect set each time. It also might be called zero times, if no display areas were added. The callee should leave the graphics context in the same state as it was found in initially, notably:

The animation client shouldn't erase screen areas in this call. That can be handled more efficiently (for some drawing styles) via RenderArea.clearAndAddArea()

Paint the current state of the enhancement. This should be called by the xlet, usually via the animation framework.

Specified by:
paintFrame in interface AnimationClient
Parameters:
gr - The graphics context to draw to, set to Src drawing mode
Throws:
java.lang.InterruptedException - if the thread has been interrupted (e.g. because the xlet is being killed)
See Also:
RenderContext.addArea(DrawRecord)

paintDone

public void paintDone()
Called when the animation framework is done painting the current frame. In each frame, the animation framework calls addDisplayAreas once, paintFrame zero or more times, and it is guaranteed to call paintDone() exactly once, even if the thread is interrupted. Note that if the thread is interrupted, it's possible paintDone() might be called without addDisplayAreas() having been called first.

Specified by:
paintDone in interface AnimationClient

handleKeyPressed

public boolean handleKeyPressed(int vkCode)
Called by the xlet when a key press is received.

A key pressis queued and true is returned only if the current segment uses that key press It is possible that the show will have a different current segment by the time the key press is processed, so there's some chance that a key press won't be queued, even if it is of interest to the state the show's about to be in. This should almost always be harmless, but if you wish to capture all key presses in your show, you can populate the needed segments with a key_pressed rc_handler with an empty body that captures all keys.

Returns:
true If the key press is enqueued, and thus is expected to be used.

internalHandleKeyPressed

public void internalHandleKeyPressed(RCKeyEvent re,
                                     Show caller)
This is an implementation method, called by RCKeyEvent


handleKeyReleased

public boolean handleKeyReleased(int vkCode)
Called by the xlet when a key release is received. Note that not all devices generate a key released.

A key release is queued and true is returned only if the current segment uses that key release. It is possible that the show will have a different current segment by the time the key release is processed, so there's some chance that a key release won't be queued, even if it is of interest to the state the show's about to be in. This should almost always be harmless, but if you wish to capture all key releases in your show, you can populate the needed segments with a key_released rc_handler with an empty body that captures all keys.

Returns:
true If the key release is enqueued, and thus is expected to be used.

internalHandleKeyReleased

public void internalHandleKeyReleased(RCKeyEvent re,
                                      Show caller)
This is an implementation method, called by RCKeyEvent


handleKeyTyped

public boolean handleKeyTyped(RCKeyEvent typed)
Called by the xlet when a key typed event is received, or generated (e.g. from a virtual keyboard). Note that not all devices generate a key released. In order to extend GRIN to support key typed events, a subclass of RCKeyEvent will be required; see the protected RCKeyEvent constructor for details.

A key typed event is queued and true is returned only if the current segment uses that key typed event.

Returns:
true If the key typed is enqueued, and thus is expected to be used.

internalHandleKeyTyped

public void internalHandleKeyTyped(RCKeyEvent re,
                                   Show caller)
This method is to be called by a subclass of RCKeyEvent that is created by someone extending GRIN to support key typed events.


handleMouseMoved

public void handleMouseMoved(int x,
                             int y)
Called by the xlet when the mouse moves. This should be called when a mouse moved event or a mouse dragged event is received.

Note that mouse events are handled synchronously, unlike remote control keypresses which are queued within the show. We assume that only fast players (like PC players) support mice.


handleMouseClicked

public void handleMouseClicked(int x,
                               int y)
Called by the xlet when the mouse is clicked.

Note that mouse events are handled synchronously, unlike remote control keypresses which are queued within the show. We assume that only fast players (like PC players) support mice.


scale

public static int scale(int value,
                        int mills)
Scale a value by a scale factor in mills. This is equivalent to
     (value * mills + 500) / 1000
 
No function is provided to recover the original value from the scaled value (and such a function would suffer from loss of precision). In a scaled show, we recommend only setting translation values, and never getting them.

See Also:
getXScale(), getYScale()

getXScale

public int getXScale()
Get the x scale factor, in mills. To scale, use scale(int, int)

See Also:
scale(int, int)

getYScale

public int getYScale()
Get the y scale factor, in mills. To scale, use scale(int, int)

See Also:
scale(int, int)

getXOffset

public int getXOffset()
Get the x offset that this show was built with. This might be useful in directors designed to work with scaled shows, but usually they act relative to the fixed coordinates within the show that will have already been adjusted.


getYOffset

public int getYOffset()
Get the y offset that this show was built with. This might be useful in directors designed to work with scaled shows, but usually they act relative to the fixed coordinates within the show that will have already been adjusted.