Example: Extending the IlvObjectInteractor class

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 and dy 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 to true 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.