2D Graphics > Managers > Manager Event Handling > View Interactors > Example of an Extension: IlvMoveInteractor
 
Example of an Extension: IlvMoveInteractor
This is a complete example of a direct subtype of the IlvManagerViewInteractor class. It allows the user to move a graphic object to another location by dragging it with the mouse. Here is the declaration of this class (it can also be found in the header file <ilviews/manager/movinter.h>):
class IlvMoveInteractor
: public IlvManagerViewInteractor
{
public:
IlvMoveInteractor(IlvManager* manager,
IlvView* view)
: IlvManagerViewInteractor(manager, view),
          _move(0) {}
 
    virtual IlBoolean handleEvent(IlvEvent& event);
virtual void handleExpose(IlvRegion* clip = 0);
virtual void drawGhost();
void drawGhost(const IlvRect&,
IlvRegion* clip = 0);
void drawGhost(IlvGraphic*, IlvRegion* clip = 0);
virtual void doIt(const IlvPoint&);
const IlvRect& getRectangle() const {return _xor_rectangle;}
protected:
IlvPos _deltax, _deltay;
IlvRect _bbox;
IlvGraphic* _move;
IlvRect _xor_rectangle;
IlBoolean _wasSelected;
void handleButtonDown(const IlvPoint&);
void handleButtonDragged(const IlvPoint&);
void handleButtonUp(const IlvPoint&);
};
This interactor lets you select and deselect objects by clicking on them with the left mouse button and the Shift key pressed. You can move an object or a set of selected objects but you cannot resize them.
The following protected fields are used in this class:
*_deltax, _deltay - Stores the distance between the mouse and the top-left corner of the objects being moved.
*_bbox - Stores the bounding box of the objects being moved.
*_move - Keeps a pointer to the object being moved.
*_xor_rectangle - Stores the rectangle dragged to mark a region.
*_wasSelected - Keeps a Boolean value indicating whether the designated object was selected before it was moved. This information is required because the object is selected when you start to move it. There are two different cases in this interactor, depending on whether one or more object is being moved. If more than one object is moved, a moving rectangle that encloses the bounding boxes of these objects is displayed. Otherwise, the moving objects themselves are displayed.
The following member function are described in this section:
*handleEvent Member Function
*drawGhost Member Function
*drawGhost for a Rectangle
*drawGhost for an Object
*doIt Member Function
*handleButtonDown Member Function
*handleButtonDragged Member Function
*handleButtonUp Member Function
handleEvent Member Function
The following code focuses on mouse events. All other events are dispatched to accelerators by a call to IlvManager::shortCut, but only if an object is not being moved at this point. This is because some accelerators might remove the object being worked on, which can be dangerous. The handleEvent() function returns IlTrue if it has consumed the event and IlFalse otherwise.
IlBoolean
IlvMoveInteractor::handleEvent(IlvEvent& event)
{
IlBoolean consumed = IlFalse;
switch (event.type()) {
case IlvButtonDown:
_xor_rectangle.w(0);
_move = 0;
if (event.modifiers() & (IlvLockModifier | IlvNumModifier)) {
consumed = IlTrue;
getManager()->getDisplay()->bell();
return consumed;
}
if (event.button() != IlvLeftButton) {
consumed = getManager()->shortCut(event, getView());
return consumed;
}
if (!event.modifiers()) {
consumed = IlTrue;
handleButtonDown(IlvPoint(event.x(), event.y()));
} else {
IlvManager* manager = getManager();
if (event.modifiers() & IlvShiftModifier) {
consumed = IlTrue;
IlvPoint p(event.x(), event.y());
IlvGraphic* obj = manager->lastContains (p,getView());
IlvDrawSelection* sel = 0;
if (obj) sel = getSelection(obj);
if (!sel && obj && manager()->isSelectable(obj)) {
manager->setSelected(!manager->isSelected(obj));
}
} else
consumed = manager->shortCut(event, getView());
}
break;
case IlvButtonUp:
if (event.button() == IlvLeftButton) {
consumed = IlTrue;
handleButtonUp(IlvPoint(event.x(), event.y()));
} else {
consumed = getManager()->shortCut(event, getView());
}
break;
case IlvButtonDragged:
if (event.modifiers() == IlvLeftButton){
consumed = IlTrue;
IlvPoint p(event.x(), event.y());
handleButtonDragged(p);
}
break;
default:
if (!_move)
consumed = getManager()->shortCut(event, getView());
break;
}
return consumed;
}
The following types of events are handled by the handleEvent member function:
*Button-Down Events
*Button-Up Events
*Button-Dragged Events
Button-Down Events
The interactor is initialized by setting _move and _xor_rectangle:
_xor_rectangle.w(0);
_move = 0;
Only the left button is handled. If the event involves another mouse button, the event is ignored and dispatched to manager accelerators:
if (event.button() != IlvLeftButton) {
consumed = getManager()->shortCut(event, getView());
return consumed;
}
The handleButtonDown member function is called if there is no event modifier:
if (!event.modifiers()) {
consumed = IlTrue;
handleButtonDown(IlvPoint(event.x(), event.y()));
}
If the Shift modifier is set, the selection state of the object pointed to by the mouse is toggled:
if (event.modifiers() & IlvShiftModifier) {
consumed = IlTrue;
IlvPoint p(event.x(), event.y());
IlvGraphic* obj = manager->lastContains(p, getView());
IlvDrawSelection* sel = 0;
if (obj) sel = getSelection(obj);
if (!sel && obj && manager()->isSelectable(obj)) {
manager->setSelected(!manager->isSelected(obj));
}
}
Button-Up Events
If the event comes from the left button, handleButtonUp is called. Otherwise, the event is dispatched to accelerators.
case IlvButtonUp:
if (event.button() == IlvLeftButton) {
consumed = IlTrue;
handleButtonUp(IlvPoint(event.x(), event.y()));
} else {
consumed = getManager()->shortCut(event, getView());
}
break;
Button-Dragged Events
The handleButtonDragged member function is called, but only if the event comes from the left button.
case IlvButtonDragged:
if (event.modifiers() == IlvLeftButton){
consumed = IlTrue;
IlvPoint p(event.x(), event.y());
handleButtonDragged(p);
}
break;
drawGhost Member Function
This member function is split in three parts: the common part, which is the entry point from the member function handleEvent, and two others, depending on the type of translation being done.
If there is only one selected object, a specific drawGhost is called for this object. Otherwise, another drawGhost function that handles a rectangle is called:
void
IlvMoveInteractor::drawGhost()
{
if (!_xor_rectangle.w()) return;
if (manager()->numberOfSelections() == 1)
drawGhost(_move);
else
drawGhost(_xor_rectangle);
drawGhost for a Rectangle
This member function is called if there is more than one selected object. It displays the bounding box of all the selected objects being moved in the view. The palette of the IlvManager object is used:
void
IlvMoveInteractor::drawGhost(const IlvRect& rect, IlvRegion* clip)
{
if (!rect.w()) return;
IlvManager* manager = getManager();
if (clip) manager->getPalette()->setClip(clip);
getView()->drawRectangle(manager->getPalette(),rect);
if (clip) manager->getPalette()->setClip();
}
drawGhost for an Object
This member function is called if there is only one selected object. It displays the object at its new coordinates by calling the draw member function after its palette has been set to XOR mode. The new coordinates are computed from the difference between the coordinates of the rectangle being dragged and the coordinates of the original bounding box of the object:
void
IlvMoveInteractor::drawGhost(IlvGraphic* obj, IlvRegion* clip)
{
if (!getManager()->isMoveable(obj) || !_xor_rectangle.w())
return;
IlvPos tempdx, tempdy;
if (getTransformer()) {
IlvRect r1(_xor_rectangle);
IlvRect r2(_bbox);
getTransformer()->inverse(r1);
getTransformer()->inverse(r2);
tempdx = r1.x() - r2.x();
tempdy = r1.y() - r2.y();
} else {
tempdx = _xor_rectangle.x() - _bbox.x();
tempdy = _xor_rectangle.y() - _bbox.y();
}
obj->translate(tempdx, tempdy);
obj->setMode(IlvModeXor);
obj->draw(getView(), getTransformer(), clip);
obj->setMode(IlvModeSet);
obj->translate(-tempdx, -tempdy);
}
doIt Member Function
The doIt member function must apply the translation to all selected objects. The delta parameter gives the translation vector expressed in the view coordinate system so it must be converted to the object coordinate system. Then the objects must be translated. This cannot be done by calling the IlvGraphic member functions directly; it must be done by the manager. Here, IlvManager::applyToSelections calls TranslateObject for each selected object:
void
TranslateObject(IlvGraphic* object, IlAny argDelta)
{
IlvPoint* delta = (IlvPoint*)argDelta;
object->translate(delta.x(), delta.y());
}
 
void
IlvMoveInteractor::doIt(const IlvPoint& delta)
{
IlvPoint origin(0, 0),
tdelta(delta);
if (getTransformer()) {
getTransformer()->inverse(origin);
getTransformer()->inverse(tdelta);
}
IlvPoint dp(tdelta.x()-origin.x(),
tdelta.y()-origin.y());
getManager->applyToSelections(TranslateObject, &dp);
}
handleButtonDown Member Function
The handleButtonDown member function selects the object to be moved, storing its previous state in _wasSelected. Then, it computes the _bbox field by means of a call to the ComputeBBoxSelections function. This function returns in _bbox the bounding box of all the selected objects:
static void
ComputeBBoxSelections(IlvManager* manager, IlvRect& bbox, IlvView* view)
{
bbox.resize(0, 0);
IlUInt nbselections;
IlvGraphic** objs = manager->getSelections(nbselections);
IlvRect rect;
IlvTransformer* t = manager->getTransformer(view);
for (IlUInt i=0; i < nbselections; i++) {
objs[i]->boundingBox(rect, t);
bbox.add(rect);
}
}
void
IlvMoveInteractor::handleButtonDown(const IlvPoint& p)
{
IlvGraphic* obj = getManager()->lastContains(p, getView());
if (!obj) return;
IlvDrawSelection* sel = manager()->getSelection(obj);
if (!sel && getManager()->isSelectable(obj)) {
getManager()->deSelect();
getManager()->makeSelected(obj);
_wasSelected = IlFalse;
sel = getManager()->getSelection(obj);
} else
_wasSelected = IlTrue;
if (sel) {
ComputeBBoxSelections(getManager(), _bbox, getView());
_move = obj;
_deltax = _bbox.x() - p.x();
_deltay = _bbox.y() - p.y();
}
}
The ComputeBBoxSelections section is described in more detail.
The first part initializes the result to the empty rectangle, and then queries the manager for all the selected objects. nbselections is the number of selected objects in the array objs:
bbox.resize(0, 0);
IlUInt nbselections;
IlvGraphic** objs = manager->getSelections(nbselections);
The next part starts a loop to scan every object:
IlvRect rect;
for (IlUInt i=0; i < nbselections; i++) {
This next part reads the bounding box of each object, transformed in the view coordinate system, and adds it to the result:
objs[i]->boundingBox(rect, t);
for (IlUInt i=0; i < nbselections; i++) {
objs[i]->boundingBox(rect, t);
bbox.add(rect);
}
handleButtonDragged Member Function
If there is a moving object and if it is moveable, the dragging position is snapped to the manager grid (if one exists) and a new _xor_rectangle is computed. Then, the member function ensureVisible makes sure that the point the user drags will remain on the visible part of the view:
void
IlvMoveInteractor::handleButtonDragged(const IlvPoint& point)
{
if (!_move) return;
IlvPoint p = point;
IlvRect rect;
if (getManager()->isMoveable(_move)) {
if (_xor_rectangle.w()) drawGhost();
p.translate(_deltax, _deltay);
getManager()->snapToGrid(getView(), p);
p.translate(-_deltax, -_deltay);
_xor_rectangle.move(p.x() + _deltax, p.y() + _deltay);
_xor_rectangle.resize(_bbox.w(), _bbox.h());
ensureVisible(p);
drawGhost();
}
}
handleButtonUp Member Function
If there are objects to move, they are translated by calling the member function doIt. Otherwise, the last designated object is deselected:
void
IlvMoveInteractor::handleButtonUp(const IlvPoint&)
{
if (!_move) return;
IlvDrawSelection* sel = getManager()->getSelection(_move);
if (_move && _xor_rectangle.w() && sel) {
drawGhost();
IlvDeltaPoint delta(_xor_rectangle.x() - _bbox.x(),
_xor_rectangle.y() - _bbox.y());
_xor_rectangle.w(0);
_move = 0;
doIt(delta);
} else {
_xor_rectangle.w(0);
_move = 0;
if (sel && _wasSelected) getManager()->deSelect();
}
}

Version 6.3
Copyright © 2018, Rogue Wave Software, Inc. All Rights Reserved.