HwBasicUserPlugIn
,
HwVisualUserPlugIn
,
BasicRunService
, and
VisualRunService
.
Plug-in modules that do processing only are called basic plug-ins.
Plug-in modules with a visual component or interface that appears in the Layout view
—and in the applet window—are
called visual plug-ins.
Basic plug-ins must subclass the Java class
HwBasicUserPlugIn
.
Visual plug-ins must subclass the Java class
HwVisualUserPlugIn
.
Methods in the basic class enable Hyperwire to communicate with the plug-in.
Additional methods in the visual class handle the plug-in�s graphics and graphical interface.
In general, you should override only those methods
that require special behavior on the part of your plug-in,
and use the default implementation of all other methods.
For example, most modules will override piEventAppletStart()
,
and most visual modules will override piDraw()
.
In addition to the basic and visual plug-in classes, two run-service interfaces enable plug-ins to obtain services and information from the Hyperwire runtime classes, send data through output ports, and so on. The run services are divided into a basic run service and a visual run service. These interface classes are implemented as part of Hyperwire; your plug-in can call their methods as it requires.
A plug-in inherits a single member variable called mRunService
,
which implements one or both of the run service interfaces.
The plug-in must use this variable to call back into the Hyperwire runtime class library.
For example, to make a call that sends a value through an output port, you might use
mRunService.siPerformOutput("My Port", MyValue);
The value of mRunService
is set in the base class
implementation of piInit()
.
Make sure that plug-ins that override piInit()
call super.piInit()
to ensure that this variable is initialized correctly.
Method names in the basic and visual plug-in classes begin with the
characters pi
for plug-in.
Method names in the basic and visual run service interfaces begin with
the characters si
for service interface.
piInit()
|
Called once to initialize the plug-in.
The method should initialize its data, including module-specific
properties,
but it can�t assume that other modules are running yet.
All modules in the applet receive this call. |
piEventAppletInit()
|
Called when the applet player initializes the applet with an init() call.
It sends a handle for the applet to the plug-in module.
All modules in the applet receive this call, which is preceded
by a call to piInit() .
|
piRestoreInitialRunState()
|
Called when the plug-in is reset. This occurs at the following times:
This method should reinitialize the plug-in�s data, as necessary. |
piEventAppletStart()
|
These three methods correspond exactly to start() , stop() ,
and destroy() calls from the applet player.
They are unconditionally sent to all modules in the applet.
Note: Different browsers and applet players manage the applet
life cycle in different ways, so the events that cause these functions to
be called vary from browser to browser.
In particular, calls to |
Some kinds of events, such as refresh ticks and key up or key down, are broadcast only to modules that register a request to receive them.
The following two tables show the plug-in methods that correspond to events. See "Visual Plug-In Modules" for more description of how a plug-in implements a visual module.
piEventParentMovedBy()
|
Sent to all descendant modules of a container module that the user moved. |
piEventRefreshDaemonTick()
|
The refresh daemon is a Hyperwire object that
generates up to 100 ticks per second.
Modules that support animated images should use the refresh daemon
to synchronize themselves with other Hyperwire modules.
A plug-in doesn't receive this call unless it requests use of the daemon by calling siPlugInWantsTicks() .
All modules registered to use the daemon receive this call when a tick occurs.
|
piEventKeyDown()
|
A plug-in doesn�t receive these calls unless it requests
notification of key events by calling
siPlugInWantsKeyEvents() .
All modules registered to receive key events receive this call
when the applet user presses or releases a key.
Key events are not broadcast if input focus is currently on a UI widget such as an edit box. |
piEventAboutToShow()
|
These calls notify a visual module that it is about to become visible or hidden. |
piEventDragTranslate()
|
Tells a visual module that the user has dragged it. Corresponds to the standard output port End Position Change. |
piMouseDown()
|
Corresponds to the standard Button Down output port. |
piMouseUp()
|
Corresponds to the standard Button Up output port. |
piMouseEntered()
|
Corresponds to the standard Mouse Enter output port. |
piMouseLeft()
|
Corresponds to the standard Mouse Leave output port. |
piMouseMove()
|
Tells the plug-in that the mouse has moved within its bounding box. |
piMouseDrag()
|
Tells the plug-in that the user has dragged within its bounding box. (Applies only to nonmovable modules.) |
The drag-translate and mouse event calls let a visual module react
to the user's mouse actions. A plug-in should override only those
methods that call for special behavior. For example, a plug-in might
have an implementation of piMouseUp() so it can respond
to mouse clicks within its area, but ignore the other kinds of mouse
activity.
|
kxInputPorts
block of the plug-in�s MDF file.
kxInputPortsMenu
block of the plug-in�s MDF file.
This defines the appearance—the order and layout—of the plug-in module�s Input Ports pop-up menu. The menu should include the default Hyperwire input ports options.
The method should implement the desired behavior of the input port, using the parameters (if any) passed to it. The method can also return a value.
Input methods must be public because the Hyperwire runtime calls them. Input method parameters can be of any type. However, the user (author) can specify literal constants only for data types that are recognized by Hyperwire. If you want the author to be able to specify literal constants in the Wire editor, make sure the parameter type is supported by Hyperwire. For example, an author can�t specify a constant for a custom class. Types that can be constant parameters fall into two categories: Java types and native Hyperwire types. The native Hyperwire types require special handling.
Java Types that Can Be Constant Parameters in Hyperwire:
byte |
char |
double |
float |
int |
long |
short |
boolean |
java.lang.String |
Point |
Rectangle |
Color |
java.net.URL |
Font |
ReferencePoint |
Hyperwire Types that Can Be Constant Parameters in Hyperwire:
You can use a number of Hyperwire classes as data types for ports and wires.
If you declare an input port that uses one of these Hyperwire data types,
you must "unwrap" the object to get the Java object it contains.
All of the Hyperwire data types have a method called getValue()
that returns the Java version of the datum.
For example:
int j; j = HwInt.getValue();
Hyperwire Type | Description |
---|---|
HwInt |
Integer |
HwFloat |
Single-precision floating point |
HwBoolean |
Boolean |
HwPoint |
2D point |
HwRectangle |
Rectangle |
HwColor |
RGB color |
Point3 |
3D point |
HwMatrix |
Array |
These are the same classes you use to "wrap" native Java objects before passing
them with the siPerformOutput()
family of functions, as described in
the next section.
kxOutputPorts
block of the plug-in�s MDF file.
kxOutputPortsMenu
block of the plug-in�s MDF file.
siPerformOutput()
.
This method is overloaded to let you send a variable number of parameters.
siGetOutput()
for a description of this technique.
Hyperwire "wraps" all data passed on wires in special Hyperwire classes.
You can�t pass Java values directly to siPerformOutput()
.
Instead, you must "wrap" data so it has a type that other Hyperwire modules can handle.
The Hyperwire runtime provides a number of wrapper classes for casting output data.
Type to send | Hyperwire wrapper | Example |
---|---|---|
int |
HwInt |
HwInt.from(1996) |
long |
HwLong |
HwLong.from(123456789000L) |
char |
HwCharacter |
HwCharacter.from('X') |
String |
HwString |
HwString.from("Vout") |
boolean |
HwBoolean |
HwBoolean.from(true) |
RGB color data | HwColor |
HwColor.from(255, 0, 0) |
Double-precision floating point | HwDouble |
HwDouble.from(3.0) |
Single-precision floating point | HwFloat |
HwFloat.from(3.0F) |
Logical font | HwFont |
HwFont.from("Helvetica") |
Module reference | HwModule |
HwModule.from(myParent) |
Point |
HwPoint |
HwPoint.from(new Point(1,2)) |
Rectangle |
HwRectangle |
HwRectangle.from(new Rectangle(0, 0, 320, 240)) |
All other classes | HwLObject |
HwLObject.from(new Foo()) |
Note: When a standard output event such as
mouse up, mouse down, and so on, occurs, the Hyperwire runtime
calls the appropriate method in the applet's visual modules.
If you want your plug-in to send output data when one of these events
occurs, override the corresponding method and call siPerformOutput()
in your implementation. See "Event Handling."
To create properties specific to your plug-in, do the following:
kxPersistentFields
block of the plug-in�s MDF file.
This tells Hyperwire the data type of each field, the method that updates it, its label in the Properties dialog, and its initial, default value.
piInit()
call),
it should allocate space for fields it stores dynamically.
At initialization time, following the call to piInit()
,
the Hyperwire runtime initializes each field by calling the method
specified for it in the MDF file.
See WavyPlugIn.java for an example of a plug-in class that that has module-specific fields.
If the plug-in itself changes the value of a module-specific field,
it must call
siPropertiesChanged()
to notify Hyperwire that the value changed.
The method should take a single argument that is the field�s new value, and assign it to the runtime variable that corresponds to it.
The Hyperwire runtime calls the field�s method
at initialization time (following piInit()
),
and when the plug-in is reset (piRestoreInitialRunState()
).
By convention, names for these methods begin with the characters
fi
for field input.
They are also known as accessor methods.
Note: You can specify a URL as a module-specific property.
Your plug-in code can treat the URL as a string.
Its MDF entry casts the string to a URL in Java.
You set the field as you do other fields, in the
kxPersistentFields
block of your plug-in's MDF file.
For example:
kxPersistentFields = { . . . kxField MyURL = { kxPublicNames = "My URL" kxJavaSignature = "LURL" kxAccessor = fiSetMyURL kxDefault = URL("mypagehere.htm") kxDescription = "HTML page" } . . . }
In your plug-in's accessor method, treat the URL as a string. For example:
public void fiSetMyURL(String url)
HwVisualUserPlugIn
.
This class extends the basic plug-in class with methods that are
specific to visual modules. Several MDF file entries also help you
to set up a visual module.
A visual module must refresh its image when the user moves it, when it has been obscured by another window and must be redisplayed, and so on. Usually the system handles most of the repainting, but it requires some assistance from the plug-in code. There are two main ways for a visual plug-in to handle refresh situations:
piDraw()
This is the simpler way to refresh the visual interface.
The Hyperwire runtime calls piDraw()
when a
refresh is required.
Override piDraw()
so that it calls
siGetAbsRect()
to find its bounding
box relative to the applet, and then call
siPaintRect()
to paint either the
module's entire bounding box or the intersection of the
bounding box and the clipping rectangle that is passed
to piDraw()
.
(The second method requires more computation but improves
performance if the plug-in uses large images.)
If you use piDraw()
to handle refresh situations,
don't override piEventParentMovedBy
or
piSetAbsRect()
.
piEventParentMovedBy()
and
piSetAbsRect()
In general, only visual plug-ins that use dialog widgets from the Java Abstract Window Toolkit (AWT) need to use this technique. This includes modules that run in a subwindow.
The Hyperwire runtime calls piEventParentMovedBy()
when the user moves the plug-in module's parent container.
It calls piSetAbsRect()
when the user moves the
plug-in module itself.
Override these methods to directly update the image and internal
state of the plug-in, using AWT methods rather than siPaintRect()
.
The method piSetAbsRect()
receives a clipping rectangle
that describes the portion of the plug-in that needs to be refreshed.
If you use piEventParentMovedBy()
and
piSetAbsRect()
, don't override
piDraw()
.
(The similarly named run-service method siSetAbsRect()
is not required for refresh handling.
It lets a plug-in change its size or position within the applet.)
Note: The visual method piEventDragTranslate()
notifies the plug-in that the user has dragged the module.
This call is not a refresh request, but the plug-in
can override this method so it returns a clipping rectangle
describing the area that needs to be repainted because of the drag.
The Hyperwire runtime then uses this information to initiate
a refresh request.
Clipping rectangles, mouse coordinates, bounding boxes, and so on are
given in pixel coordinates relative to the applet, with (0,0) as the
upper-left corner of the applet window.
The exceptions are the visual run service methods siGetRelativeRect()
,
which returns the plug-in's bounding rectangle relative to its parent, and
siGetScreenRect()
, which returns the plug-in's position relative to
the entire screen.
For these methods, (0,0) is still the upper-left corner, either of the parent
or the screen.
The MDF entries for setting up visual plug-in modules are as follows:
kxSize
|
Sets the default module size in the Layout view. |
kxIsFixedSize
|
Specifies whether the author can change the module size. |
kxDrawImage
|
Specifies an image to draw within the module. |
kxDrawImageIsStretched
|
Specifies whether the image is stretched to fit kxSize .
|
kxDrawImageIsLabelled
|
Specifies a label that appears within the module in Layout view. |
kxDrawImageHasBorder
|
Specifies whether the module appears within a border. |
kxLayoutViewProxyClass
|
For visual modules, must be set to equal AuthorRepresentation .
|
You can set up modules that are mutually exclusive within an applet or container. That is, only one module is active at a time, as with the radio buttons used by many dialogs. You do this as follows:
siSetRadioNodule()
when it needs to become active.
The Hyperwire runtime then calls the
piSetRadioState()
method of all modules in the same container.
The modules receive a state
parameter of false
,
except for the one that called siSetRadioNodule()
.
You can choose which event triggers the siSetRadioNodule()
call.
For example, a plug-in�s piMouseUp()
method might use the mouse-up event
to call siSetRadioNodule()
and make itself the active module.
piSetRadioState()
method.
This method should make the module active if it receives true
,
or make the module inactive if it receives false
.
This ensures that only one module is active at a time.
siPlugInWantsTicks()
to register the plug-in as one that wants ticks.
Put the call to siPlugInWantsTicks()
in your implementation of
piEventAppletInit()
to make sure that it happens at initialization time.
piEventRefreshDaemonTick()
so that it checks the current time and adjusts its state and refreshes the screen
accordingly.
When the plug-in is registered, the Hyperwire runtime calls
piEventRefreshDaemonTick()
every time a tick occurs.
The implementation of piEventRefreshDaemonTick()
must store
the time value (timeNow
) passed to it.
When it is called again, it must compare the new time value with the previous
value.
Based on the amount of time that has elapsed, the method must do what it
needs to do to synchronize itself.
For example, if the plug-in displays multiple frames of an animation,
a long elapsed time might cause it to skip some frames.
If the plug-in is a visual plug-in, piEventRefreshDaemonTick()
must notify the refresh daemon of the area that it has changed visually.
It does this by calling the refresh daemon's invalidateRect()
method.
This method can take either a Rectangle
object or a set of two
points:
public synchronized void invalidateRect(Rectangle r); public void invalidateRect(int x, int y, int width, int height)
For example:
public void piEventRefreshDaemonTick(long timeNow, RefreshDaemon worldMover) throws HwException { if (prevTime < 0) // Initialized to -1 so this is evaluated // only the first time this function is called prevTime = timeNow; long deltaTime = timeNow - prevTime; prevTime = timeNow; Rectangle newBox = ((VisualRunService) mRunService).siGetAbsRect(); // Do processing here to make the plug-in "catch up" . . . worldMover.invalidateRect(Box); }
The sample plug-in WavyPlugIn.java shows a more extensive example of synchronizing animation using the refresh daemon.
kxFlags = kxEmbeddedWindow
This kxFlags
setting notifies Hyperwire that the plug-in is implemented as a subwindow
that is always on top of other, non-window modules.
You must manage the subwindow using the Java Abstract Window Toolkit (AWT).
Hyperwire doesn't use piDraw()
to refresh the subwindow.
A plug-in module can subclass a standard Hyperwire module. To do so, go through the following steps:
kxPlugInClass
field names your class instead of the standard class.
RedFieldsEditor
to the list of Properties dialog
editors (tabs) in the MDF file's
kxPropertyEditors
entry.
This controls the Properties dialog tab for module-specific settings.
If your plug-in has module-specific settings, its class must contain accessor methods
that match the kxPersistentFields
block in the MDF file.
kxInputPorts
)
and outputs (kxOutputPorts
) you are adding
to the class.
For an example of a subclassed Hyperwire object, in this case Circle, see WobblyCirclePlugIn.java. The MDF file for this document is in Wobble Ellipse.mdf.