The MoveObjectInteractor class defined
in this example allows you to move an object using the mouse,
moving it to where you release the mouse button. You can see the
complete code of the example in MoveObjectInteractor.java
located at codefragments/interactors/moveobjinter/src/MoveObjectInteractor.java
in the installed product. For details, see <installdir> /jviews-framework810/codefragments/interactors/moveobjinter/index.html.
public class MoveObjectInteractor extends IlvObjectInteractor { private IlvRect mrect; private float dx, dy; private boolean dragging = false; /** Creates an moveObjectInteractor. */ public MoveObjectInteractor() { super(); } ... }
The
MoveObjectInteractor
extends the
IlvObjectInteractor
class and defines the following attributes:
- The attribute
mrect
specifies the future bounding rectangle of the graphic object. This data is updated each time the object is dragged. - The
dx
anddy
attributes represent the translation from the original clicked point and the current top-left corner of the graphic object. - The Boolean value
dragging
is set totrue
when the user starts dragging the object.
Events are processed by the
processEvent
method as follows.
protected boolean processEvent(IlvGraphic obj, AWTEvent event, IlvObjectInteractorContext context) { switch (event.getID()) { case MouseEvent.MOUSE_PRESSED: return processButtonDown(obj, (MouseEvent)event, context); case MouseEvent.MOUSE_DRAGGED: return processButtonDragged(obj, (MouseEvent)event, context); case MouseEvent.MOUSE_RELEASED: return processButtonUp(obj, (MouseEvent)event, context); default: return false; } }
The
processEvent
method dispatches events to three different methods depending on
their type. The
processEvent
method takes a graphic object, an event, and a context as its
parameters. The context IlvObjectInteractorContext is an interface
that must be implemented by classes allowing the use of an object
interactor. The class IlvManager takes care of that and passes a
context object to the object interactor. From a context, you can
get a transformer, change the mouse cursor, and so on.
In the
processButtonDown
method, the distance between the clicked point and the current
top-left corner of the graphic object is stored in the attributes
dx
and
dy
. Note that the positions are stored in the view coordinate system.
The top-left corner of the graphic object is extracted from the
bounding rectangle of the object, which is computed using the
boundingBox
method. The
invalidateGhost
method is then called. This method requests the interactor context
to redraw the region corresponding to the current bounding
rectangle (stored in
mrect
).
public boolean processButtonDown(IlvGraphic obj, MouseEvent event, IlvObjectInteractorContext context) { if ((event.getModifiers() & InputEvent.BUTTON2_MASK) != 0 || (event.getModifiers() & InputEvent.BUTTON3_MASK) != 0) return true ; if (dragging) return true ; dragging = true; IlvPoint p = new IlvPoint(event.getX(), event.getY()); mrect = obj.boundingBox(context.getTransformer()); dx = p.x - mrect.x; dy = p.y - mrect.y; invalidateGhost(obj, context); return true; }
The
invalidateGhost
method is implemented as follows..
private void invalidateGhost(IlvGraphic obj, IlvObjectInteractorContext context) { if (obj == null || context == null) return; if (mrect == null || mrect.width == 0 || mrect.height == 0) return; IlvRect invalidRegion = new IlvRect(mrect); context.repaint(invalidRegion); }
The principle for drawing and erasing the ghost is as follows: the
interactor invalidates regions of the context (the bounds of the
ghost before and after any position change), while the drawing of
the ghost is only performed when requested by the drawing system.
Indeed, the method
handleExpose
, defined on the base class
IlvObjectInteractor
, is called only when the view is redrawn. This method, whose
implementation in the base class does nothing, is overridden as
follows.
public void handleExpose(IlvGraphic obj, Graphics g, IlvObjectInteractorContext context) { drawGhost(obj, g, context); }
The actual drawing of the ghost is done by the following
drawGhost
method..
protected void drawGhost(IlvGraphic obj, Graphics g, IlvObjectInteractorContext context) { if (mrect != null) { g.setColor(context.getDefaultGhostColor()); g.setXORMode(context.getDefaultXORColor()); IlvTransformer t = context.getTransformer(); IlvRect r = obj.boundingBox(t); IlvTransformer t1 = new IlvTransformer(new IlvPoint(mrect.x - r.x, mrect.y -r.y)); t.compose(t1); obj.draw(g, t); } }
The
Graphics
object that is passed as an argument is set to XOR mode using the
default XOR color and ghost color defined in the context. Next, a
transformer is computed that will draw the object in the desired
location given by
mrect
. Note that the object is not translated, only drawn at another
location. The method does nothing if
mrect
is
null
. This prevents the ghost from being drawn if the
drawGhost
method happens to be called after the end of the interaction.
Mouse dragged events are handled as follows.
protected boolean processButtonDragged(IlvGraphic obj, MouseEvent event, IlvObjectInteractorContext context) { if (!dragging || mrect == null) return false; IlvPoint p = new IlvPoint(event.getX(), event.getY()); invalidateGhost(obj, context); mrect.move(p.x - dx, p.y - dy); IlvTransformer t = context.getTransformer(); if (t != null) t.inverse(mrect); context.ensureVisible(p); t = context.getTransformer(); if (t != null) t.apply(mrect); invalidateGhost(obj, context); return true; }
First the current ghost is invalidated by a call to
invalidateGhost
. The new required location is changed to the position of the
mouse, translated with the original translation.
mrect.move(p.x - dx, p.y - dy);
Then
ensureVisible
is called on the context. If the current dragged point is outside
the visible area of the view, this will scroll the view of the
manager so that the dragged point becomes visible. This operation
may change the transformer of the view, as the value of
mrect
is stored in the coordinate system of the view. Before
ensureVisible
is called, the value of
mrect
is transformed to the manager coordinate system as follows.
IlvTransformer t = context.getTransformer(); if (t != null) t.inverse(mrect);
After the call to
ensureVisible
, the value of
mrect
is transformed back to the view coordinate system as follows.
t = context.getTransformer(); if (t != null) t.apply(mrect);
The actual moving of the graphic object is
done when the mouse button is released. The mouse released event is
handled like this:
protected boolean processButtonUp(IlvGraphic obj, MouseEvent event, IlvObjectInteractorContext context) { if (!dragging || mrect == null) return true; dragging = false; invalidateGhost(obj, context); doMove(obj, context); mrect = null; return true; }
First the ghost is invalidated by calling the
invalidateGhost
method. Then the
doMove
method is called. This method updates the position of the graphic
object according to the final coordinates of
mrect
. After moving the object,
mrect
is set to
null
to prevent further drawing of the ghost.
The implementation of the method
doMove
is as follows
void doMove(IlvGraphic graphic, IlvObjectInteractorContext context) { if (mrect == null) return; IlvTransformer t = context.getTransformer(); if (t != null) t.inverse(mrect); graphic.getGraphicBag().moveObject(graphic, mrect.x, mrect.y, true); }
The value of
mrect
is translated to the coordinate system of the manager as follows.
IlvTransformer t = context.getTransformer(); if (t != null) t.inverse(mrect);
You should never try to change the position or the shape of a
managed graphic object directly (or, more precisely, to modify its
bounding box), for example by calling methods of the graphic object
directly. Such changes must be done through a function, in this
case
moveObject
, which is applicable to managers and takes all the necessary
precautions. For further information, see Modifying geometric properties of objects.