Managing input events
Describes how to handle input events using an interactor on the view or on a graphic object.
Describes various ways of handling input events.
Describes how to use object interactors for associating specific behavior with an object or set of objects.
Describes how to extend the
IlvObjectInteractor class with an example for dragging an object to a new position.
Describes how to create a new graphic object with customized interactions.
Describes how to use view interactors to handle view behavior.
Describes the relationships between classes used for interactors and selection.
Describes how to be notified when the active interactor of a view changes with an example of implementing a drag rectangle.
Describes the predefined view interactor functionality and how to customize it.
Describes how to implement tooltips and pop-up menus on graphic objects in Swing applications.
Handling input events
There are two ways of handling the input events in a manager view:
An interactor can be set to the view using the class
IlvManagerViewInteractor, which will handle all the events that occur on a view.
An
object interactor can be used on a graphic object. This interactor, an instance of the class
IlvObjectInteractor, handles the events occurring on a particular object if no view interactor has been installed on the view.
Tooltips and pop-up menus are handled by the
IlvToolTipManager and
IlvPopupMenuManager central managers. They are neither manager view interactors nor object interactors.
IlvToolTipManager and
IlvPopupMenuManager listen for the specific events that trigger tooltip or a pop-up menu display in the registered view.
Object interactors
When you want to associate a specific behavior with an object, you can use an object interactor (class IlvObjectInteractor and its subclasses). Whenever an event is received by a manager view that has no associated view interactor, the manager attempts to send it to an object by a call to an attached object interactor. If there is an object at the event location, and if this object is connected to an object interactor, the manager sends the event to that interactor. If the interactor does not manage this event, or if the situation is not applicable, the manager tries to handle the event by means of accelerators.
You can create an IlvObjectInteractor instance and bind it to an object or a set of objects using the IlvGraphic method setObjectInteractor. As soon as this binding occurs, the object receives user events and deals with them, therefore it is the interactor and not the object itself that manages these events.
Querying, setting, or removing an object interactor can be done by means of calls to the following methods on the IlvGraphic instance:
IlvObjectInteractor getObjectInteractor()
void setObjectInteractor(IlvObjectInteractor interactor)
An instance of
IlvObjectInteractor can be shared by several graphic objects. This allows you to reduce the amount of memory needed to handle the same interaction on a large number of graphic objects. To share the same object interactor instance, do not use
new to create your interactor; use the
Get method of the class
IlvObjectInteractor.
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-framework/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.
Customizing the interactor of selected graphic objects
The selection interactor (
IlvSelectInteractor) is also a view interactor. It allows you to select, move and reshape objects. The way an object is reshaped depends on the type of the object. For example, you can only change the size of an
IlvRectangle while you can add points to an
IlvPolygon. The dependency of possible interaction on the type of the object comes through the object interactor associated with the selection of the object.
If an object is selected, the method
makeSelection is called to create a suitable selection object (subclass of
IlvSelection. This selection object is drawn on top of the selected object.
Since IlvSelection is a subclass of IlvGraphic, it can have object interactors. When the select interactor moves the mouse over a selection object, it queries for the object interactor of the selection object.
If no object interactor is explicitly set on the selection object, it calls
getDefaultInteractor to retrieve the class name of the default interactor and then sets the object interactor of the selection object to an instance of the default interactor.
Then, the select interactor forwards all events to the object interactor of the selection. This object interactor receives the mouse events as long as the mouse is over the selection object.
The object interactor of the selection object can react on the received events by reshaping the original selected object. In most cases, the object interactor of the selection object does not modify the selection object itself but rather the original selected object.
To create a new graphic object class with a customized selection and customized reshape interaction on the selected object:
3. Create a new object interactor that works on the selected object instead of the selection.
4. In the derived graphic class, override
makeSelection to return an instance of the new selection class.
5. In the new selection class, override
getDefaultInteractor to return the class name of the new object interactor. Alternatively, you could call
setObjectInteractor on the selection object when it is allocated. The former is more convenient if you want to have the same interactor for all instances of the new subclass of
IlvSelection, while the latter can be used if you want to assign specific object interactors to specific instances of the selection.
The following code example creates a new graphic subclass MyMarker that has a special selection object MyMarkerSelection. If the marker is selected, the object interactor MyMarkerEdition becomes active. Each click in the marker selection changes the type (and therefore also the shape) of the marker.
public class MyMarker extends IlvMarker
{
...
public IlvSelection makeSelection()
{
return new MyMarkerSelection(this);
}
}
public class MyMarkerSelection extends IlvUnresizeableDrawSelection
{
...
public String getDefaultInteractor()
{
return MyMarkerEdition.class.getName();
}
}
public class MyMarkerEdition extends IlvReshapeSelection
{
public MyMarkerEdition()
{
super();
}
protected boolean handleButtonDown(IlvDrawSelection sel, MouseEvent event,
IlvObjectInteractorContext context)
{
// each click with the left mouse button into the selection object
// changes the type of the selected object
if ((event.getModifiers() & InputEvent.BUTTON2_MASK) != 0 ||
(event.getModifiers() & InputEvent.BUTTON3_MASK) != 0)
return true;
MyMarkerSelection msel = (MyMarkerSelection)sel;
MyMarker marker = (MyMarker)msel.getObject();
// even though the object interactor is on the selection, it
// modifies the selected object, not the selection
final int tp = (marker.getType() >= 512 ? 1: marker.getType() * 2);
IlvGraphicBag bag = marker.getGraphicBag();
if (bag == null)
marker.setType(tp);
else
bag.applyToObject(marker, new IlvApplyObject() {
public void apply(IlvGraphic g, Object arg) {
((MyMarker)g).setType(tp);
}
}, null, true);
return true;
}
}
View interactors
The
IlvManagerViewInteractor class handles view behavior. The role of this class is to handle complex sequences of user input events that are to be processed by a particular view object.
View interactor methods
You can add or remove a view interactor with the following methods:
IlvManagerViewInteractor getInteractor()
void setInteractor(IlvManagerViewInteractor inter)
void pushInteractor(IlvManagerViewInteractor inter)
IlvManagerViewInteractor popInteractor()
Predefined view interactors
Rogue Wave JViews provides predefined view interactors. Following is a list of these interactors:
IlvUnZoomViewInteractor - Allows the unzooming command. You have to draw a rectangular region into which the area you are watching is unzoomed.
IlvZoomViewInteractor - Allows the zooming command. You draw a rectangular region where you want to zoom.
Class diagrams for interactors and selection classes
The most important selection objects and corresponding object interactors summarizes the relationships between
IlvObjectInteractor and
IlvSelection. Each graphic object can have an object interactor that handles the interactions. When an object is selected by
IlvSelectInteractor, an
IlvSelection object is created for the selected object through the method
makeSelection. With the help of the selection object, the selected object can be manipulated, for example, it can be reshaped. Thus, the selection object is associated with a default object interactor. Different subclasses of
IlvSelection have different default object interactors.
The most important selection objects and corresponding object interactors
The view interactor classes shows the different subclasses of
IlvManagerViewInteractor. Most interactors are used to create certain kinds of object, such as
IlvMake. Other interactors allow the view to be zoomed in or out. The class
IlvSelectInteractor allows graphic objects to be selected. It delegates some functionality to
IlvSelectInteractorMoveSelection and
IlvSelectInteractorMultipleSelection.
The view interactor classes
Interactor listeners
When the active interactor of a view changes, the view fires an
InteractorChangedEvent event. A class must implement the
InteractorListener interface in order to be notified that a view interactor has been modified and must register itself using the
addInteractorListener method of
IlvManagerView. You can also specify that the listener no longer be notified of such events by using the
removeInteractorListener method.
When the interactor of a view changes, the view calls the interactorChanged method of the listeners.
void interactorChanged(InteractorChangedEvent event)
This method is called with an instance of the class
InteractorChangedEvent as a parameter containing information on the new and the old interactor.
Example: Implementing the DragRectangleInteractor class
This example shows how the methods of the predefined view interactor
IlvDragRectangleInteractor are implemented. You can use this example as a starting point for creating your own interactor functionality. The class
IlvDragRectangleInteractor is used to specify a rectangular region in a view. When this rectangle is selected, the
fireRectangleDraggedEvent method is called. The rectangle can then be used for various purposes in derived interactors. For example, you can create a subclass of this interactor to zoom in on the selected area. You can see the complete code example file
DragRectangleInteractor.java located at
codefragments/interactors/dragrectinter/src/DragRectangleInteractor.java in the installed product. For details, see
<installdir> /jviews-framework/codefragments/interactors/dragrectinter/srchtml/DragRectangleInteractor.java.html.
The DragRectangleInteractor class defines the following attributes: start, rectangle, and dragging.
public class DragRectangleInteractor extends
IlvManagerViewInteractor {
/** The anchor point of the rectangle. */
private final IlvPoint start = new IlvPoint();
/** The rectangle when dragging. */
private final IlvRect rectangle = new IlvRect();
/** True if dragging. */
private boolean dragging = false;
...
}
The attribute start is the point at the start of the drag action; rectangle is the rectangle that is drawn when dragging; dragging is a Boolean variable whose value is true when the user drags.
The enableEvents method called in the constructor takes the MOUSE_EVENT_MASK and MOUSE_MOTION_EVENT_MASK as parameters. Events must be enabled to be taken into account by the interactor:
public DragRectangleInteractor()
{
enableEvents(AWTEvent.MOUSE_EVENT_MASK |
AWTEvent.MOUSE_MOTION_EVENT_MASK);
}
The processMouseEvent method handles the MOUSE_PRESSED and MOUSE_RELEASED events:
protected void processMouseEvent(MouseEvent event)
{
switch (event.getID()) {
case MouseEvent.MOUSE_PRESSED:
{
if (dragging) break;
if ((event.getModifiers() & InputEvent.BUTTON2_MASK) == 0 &&
(event.getModifiers() & InputEvent.BUTTON3_MASK) == 0)
{
dragging = true;
IlvTransformer t = getTransformer();
start.move(event.getX(), event.getY());
t.inverse(start);
rectangle.width = 0;
rectangle.height = 0;
}
break;
}
case MouseEvent.MOUSE_RELEASED:
if (dragging) {
dragging = false;
drawGhost();
rectangle.width = 0;
rectangle.height = 0;
fireRectangleDraggedEvent(new IlvRect(rectangle), event);
}
}
}
When the mouse button is pressed, the mouse pointer coordinates are stored in the start variable and are converted for storage in the coordinate system of the manager. When the mouse is released, the drawGhost method of IlvManagerViewInteractor is called to erase the ghost image. The width and height of the rectangle are set to 0 to prevent further drawings of the ghost, and the fireRectangleDraggedEvent method is called to notify the end of the drag operation. The following code demonstrates the dragged rectangle.
NOTE The
drawGhost method can be used to perform a temporary drawing that gives the user feedback on the action of his present operation.
The processMouseMotionEvents handles the MOUSE_DRAGGED events:
protected void processMouseMotionEvent(MouseEvent event)
{
if (event.getID() == MouseEvent.MOUSE_DRAGGED && dragging) {
drawGhost();
IlvTransformer t = getTransformer();
IlvPoint p = new IlvPoint(event.getX(), event.getY());
ensureVisible(p);
rectangle.reshape(start.x, start.y, 0,0);
t.inverse(p);
rectangle.add(p.x, p.y);
drawGhost();
}
}
First the rectangle is erased by a call to drawGhost. The call to ensureVisible ensures that the dragged point remains visible on the screen. The new rectangle is then computed in the coordinate system of the manager and drawGhost is called to draw the new rectangle.
The drawGhost method simply draws the dragged rectangle. Since the rectangle is in the manager coordinate system, the method needs to apply the view transformer before drawing.
protected void drawGhost(Graphics g)
{
IlvRect rect = new IlvRect(rectangle);
IlvTransformer t = getTransformer();
if (t != null)
t.apply(rect);
if (rect.width > 0 && rect.height >0) {
g.drawRect((int)Math.floor(rect.x), (int)Math.floor(rect.y),
(int)Math.floor(rect.width),
(int)Math.floor(rect.height));
}
}
The selection interactor
The Rogue Wave JViews library provides a predefined view interactor,
IlvSelectInteractor, for selecting and editing graphic objects in a manager. This class allows you to:
Select an object by clicking on it.
Select or deselect several objects using Shift-Click.
Select several objects by dragging a rectangle around them.
Move one or several objects by selecting them and dragging the mouse.
Edit objects by manipulating their selection object.
The interactor can be customized to your needs, as follows:
You can enable or disable multiselection using:
public void setMultipleSelectionMode(boolean v)
You can select the mode for selecting objects by dragging a rectangle around them: opaque or ghost:
public void setOpaqueDragSelection(boolean o)
public boolean isOpaqueDragSelection()
You can select the mode for moving graphic objects: opaque or ghost:
public void setOpaqueMove(boolean o)
public boolean isOpaqueMove()
You can select the mode for resizing graphic objects: opaque or ghost:
public void setOpaqueResize(boolean o)
public boolean isOpaqueDragSelection()
You can select the mode for editing polypoint objects: opaque or ghost:
public void setOpaquePolyPointsEdition(boolean o)
public boolean isOpaqueDragSelection()
You can specify the modifier that allows multiple selection:
public void setMultipleSelectionModifier(int m)
public boolean getMultipleSelectionModifier()
You can specify the modifier that allows selection by dragging a rectangle starting from a point on top of a graphic object:
public void setSelectionModifier(int m)
public boolean getSelectionModifier()
You can allow selection of several objects using a dragged rectangle:
public void setDragAllowed(boolean v)
public boolean isDragAllowed()
You can change the ability to move objects by:
public void setMoveAllowed(boolean v)
public boolean isMoveAllowed()
You can change the ability to edit objects by:
public void setEditionAllowed(boolean v)
public boolean isEditionAllowed()
NOTE The ability to move, edit, or select an object can be controlled object by object using the properties of the object.
Many other customizations can be done by subclassing the interactor and overriding the appropriate method.
The editing of a graphic object is controlled by an object interactor. When a graphic object is selected, the manager can dispatch events occurring on the selection object to the
object interactor attached to the selection object. This object interactor is created by the selection object with a call to the
getDefaultInteractor method of the class
IlvSelection. When creating your own selection object, you can also create an object interactor to edit the selected object. The default object interactor is the class
IlvReshapeSelection, which allows the user to reshape graphic objects by pulling the handles of the objects.
Tooltips and pop-up menus on graphic objects
Rogue Wave JViews provides facilities to specify tooltips and pop-up menus for graphic objects in Swing applications. Tooltips and pop-up menus are handled by the
IlvToolTipManager and the
IlvPopupMenuManager central managers. These managers control events that trigger tooltip or pop-up menu display; they are not implemented as object interactors or manager view interactors. Tooltips and pop-up menus can be used in combination with any interactor.
Tooltips
In order to specify a tooltip for a graphic object, set the tooltip text as follows:
graphic.setToolTipText("Some Tooltip");
Tooltips only work when the view is registered with the tooltip manager. In order to enable tooltips in a manager view, register the view as follows:
IlvToolTipManager.registerView(managerView);
After a graphic object is registered, whenever a user holds the mouse over the object, the tooltip appears. When the mouse is moved away from the graphic, the tooltip disappears.
The
IlvToolTipManager relies on the Swing tooltip manager. Parameters such as the initial delay or the dismiss delay can be set on the Swing tooltip manager. For example:
IlvToolTipManager.getToolTipManager().setInitialDelay(3000);
For more information, see
IlvToolTipManager.
Pop-up menus
In order to associate a specific pop-up menu with a graphic object, create a JPopupMenu object and link it to a graphic as follows:
graphic.setPopupMenu(popupMenu);
Pop-up menus only work when the view is registered with the pop-up menu manager. In order to enable pop-up menus in a manager view, register the view as follows:
IlvPopupMenuManager.registerView(managerView);
After the pop-up menu is registered, whenever a user right-clicks the graphic, its pop-up menu appears.
Pop-up menus use a lot of memory. To avoid wasting memory, share pop-up menus between multiple graphic objects. To do this, instead of registering a pop-up menu with an individual graphic using graphic.setPopupMenu(...), register the pop-up menu directly with the pop-up menu manager by calling:
IlvPopupMenuManager.registerMenu("name", popupMenu);
You then assign this pop-up menu to graphics in the following way:
graphic1.setPopupMenuName("name");
graphic2.setPopupMenuName("name");
Pop-up menus registered with a single graphic object are active for that object only. Pop-up menus registered with the pop-up menu manager can be:
Saved in .
ivl files.
Used for cut and paste operations.
When a pop-up menu is shared, you need to know which graphic object triggered the event. The action listeners associated with pop-up menu items can retrieve the context of the pop-up menu using an
IlvPopupMenuContext object. This is done in the following way:
public void actionPerformed(ActionEvent e) {
JMenuItem m = (JMenuItem)e.getSource();
IlvPopupMenuContext context=IlvPopupMenuManager.getPopupMenuContext(m);
if (context == null) return;
IlvGraphic graphic = context.getGraphic();
IlvManagerView view = context.getManagerView();
//Do the action on this graphic for this view.
}
For more information, see
IlvPopupMenuContext and
IlvPopupMenuManager in the
Java API Reference Manual.
IlvSimplePopupMenu is a subclass of
JPopupMenu that allows you to configure pop-up menus easily. For more information, see
IlvSimplePopupMenu.
An example that illustrates the different ways of using pop-up menus is available as part of the demonstration software. For details, see
<installdir> //jviews-framework/codefragments/popupmenu/index.html.
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.