Manager > マネージャー・イベント処理 > ビュー・インタラクター > 拡張の例:IlvMoveInteractor
 
拡張の例:IlvMoveInteractor
この例は IlvManagerViewInteractor クラスの直接サブクラスの完全版です。これにより、グラフィック・オブジェクトをマウスでドラッグすることにより、他の位置に移動できます。このクラスの宣言は次のとおりです (ヘッダー・ファイル <ilviews/manager/movinter.h >でも参照できます)。
class IlvMoveInteractor
: public IlvManagerViewInteractor
{
public:
IlvMoveInteractor(IlvManager* manager,
IlvView* view)
: IlvManagerViewInteractor(manager, view),
          _move(0) {}
 
virtual void 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&);
};
このインタラクターによって、Shift キーを押しながらオブジェクトを左クリックすることにより、そのオブジェクトを選択または選択解除できます。1 つのオブジェクトまたは選択したオブジェクトのセットを移動することはできますが、サイズは変更できません。
このクラスでは、次の保護領域が使用されます。
*_deltax, _deltay - 移動対象オブジェクトの左上隅からマウスまでの距離を格納します。
*_bbox - 移動中のオブジェクトのバウンディング・ボックスを格納します。
*_move - 移動中のオブジェクトへのポインターを格納します。
*_xor_rectangle - 領域をマークするためにドラッグされた矩形を格納します。
*_wasSelected - 指定されたオブジェクトが移動前に選択されているかどうかを示すブール型を保持します。選択されたオブジェクトのみが移動されるため、この情報が必要になります。移動中のオブジェクトの数が 1 つか複数かによって、このインタラクターには 2 通りのケースがあります。複数のオブジェクトを移動する場合、これらのオブジェクトのバウンディング・ボックスを囲む移動矩形が表示されます。それ以外の場合には、移動オブジェクト自体が表示されます。
このセクションでは以下のメンバー関数について説明します。
*handleEvent メンバー関数
*drawGhost メンバー関数
*矩形用 drawGhost
*オブジェクト用 drawGhost
*doIt メンバー関数
*handleButtonDown メンバー関数
*handleButtonDragged メンバー関数
*handleButtonUp メンバー関数
handleEvent メンバー関数
以下のコードはマウス・イベントのみを取り扱います。その他すべてのイベントは IlvManager::shortCut の呼び出しによりアクセラレーターにディスパッチされますが、オブジェクトが現時点で移動されていない場合に限ります。これは、一部のアクセラレーターは現在処理中のオブジェクトを削除してしまう危険性があるためです。
void
IlvMoveInteractor::handleEvent(IlvEvent& event)
{
switch (event.type()) {
case IlvButtonDown:
_xor_rectangle.w(0);
_move = 0;
if (event.modifiers() & (IlvLockModifier | IlvNumModifier)) {
getManager()->getDisplay()->bell();
return;
}
if (event.button() != IlvLeftButton) {
getManager()->shortCut(event, getView());
return;
}
if (!event.modifiers())
handleButtonDown(IlvPoint(event.x(), event.y()));
else {
IlvManager* manager = getManager();
if (event.modifiers() & IlvShiftModifier) {
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
manager->shortCut(event, getView());
}
break;
case IlvButtonUp:
if (event.button() == IlvLeftButton)
handleButtonUp(IlvPoint(event.x(), event.y()));
else getManager()->shortCut(event, getView());
break;
case IlvButtonDragged:
if (event.modifiers() == IlvLeftButton){
IlvPoint p(event.x(), event.y());
handleButtonDragged(p);
}
break;
default:
if (!_move)
getManager()->shortCut(event, getView());
break;
}
以下の種類のイベントが handleEvent メンバー関数により処理されます。
*ボタンダウン・イベント
*ボタンアップ・イベント
*ボタンドラッグ・イベント
ボタンダウン・イベント
インタラクターは _move_xor_rectangle とを設定することにより初期化されます。
_xor_rectangle.w(0);
_move = 0;
左ボタンのみが処理されます。イベントに他のマウス・ボタンがある場合そのイベントは無視され、マネージャー・アクセラレーターにディスパッチされます。
if (event.button() != IlvLeftButton) {
getManager()->shortCut(event, getView());
return;
}
イベント・モディファイアがない場合、handleButtonDown メンバー関数が呼び出されます。
if (!event.modifiers())
handleButtonDown(IlvPoint(event.x(), event.y()));
Shift モディファイアが設定されている場合、マウスでポイントされるオブジェクトの選択状態が切り替えられます。
if (event.modifiers() & IlvShiftModifier) {
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));
}
}
ボタンアップ・イベント
イベントが左ボタンで開始された場合、handleButtonUp が呼び出されます。それ以外の場合は、イベントはアクセラレーターにディスパッチされます。
case IlvButtonUp:
if (event.button() == IlvLeftButton)
handleButtonUp(IlvPoint(event.x(), event.y()));
else getManager()->shortCut(event, getView());
break;
ボタンドラッグ・イベント
イベントが左ボタンで開始された場合に限り、handleButtonDragged メンバー関数が呼び出されます。
case IlvButtonDragged:
if (event.modifiers() == IlvLeftButton){
IlvPoint p(event.x(), event.y());
handleButtonDragged(p);
}
break;
drawGhost メンバー関数
このメンバー関数は 3 つの部分に分割されます。共通部分はメンバー関数 handleEvent のエントリー・ポイントであり、他の 2 つの部分は実行中の差分移動の種類に応じて決まります。
選択されているオブジェクトが 1 つしかない場合、特定の drawGhost がこのオブジェクトに対して呼び出されます。これ以外の場合は、矩形を処理する他の drawGhost 関数が呼び出されます。
void
IlvMoveInteractor::drawGhost()
{
if (!_xor_rectangle.w()) return;
if (manager()->numberOfSelections() == 1)
drawGhost(_move);
else
drawGhost(_xor_rectangle);
矩形用 drawGhost
複数のオブジェクトが選択されている場合、このメンバー関数が呼び出されます。ビュー内で移動中であるすべての選択オブジェクトのバウンディング・ボックスを表示します。IlvManager オブジェクトのパレットが使用されます。
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
オブジェクトが 1 つだけ選択されている場合、このメンバー関数が呼び出されます。これはパレットが XOR モードに設定された後で draw メンバー関数を呼び出すことにより、オブジェクトを新しい座標に表示します。新しい座標は、ドラッグ中の矩形の座標とオブジェクトの元のバウンディング・ボックスの座標の差から算出されます。
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 メンバー関数
doIt メンバー関数は、選択されたすべてのオブジェクトに差分移動を適用する必要があります。delta パラメーターはビュー座標系で表現した差分移動ベクトルを提供するため、オブジェクト座標系に変換する必要があります。その後、オブジェクトを差分移動します。IlvGraphic メンバー関数を直接呼び出しても実行できません。マネージャーから実行する必要があります。ここで IlvManager::applyToSelections は、選択されたそれぞれのオブジェクトに対して TranslateObject を呼び出します。
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 メンバー関数
handleButtonDown メンバー関数は移動するオブジェクトを選択し、オブジェクトの前の状態を _wasSelected に格納します。次に、ComputeBBoxSelections 関数を呼び出すことにより、_bbox フィールドを計算します。この関数は _bbox に、選択されたすべてのオブジェクトのバウンディング・ボックスを戻します。
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();
}
}
詳細については、ComputeBBoxSelections を参照してください。
最初の部分は結果を空の矩形に初期化し、次に、選択されたすべてのオブジェクトをマネージャーに問い合わせます。配列 objs nbselections は選択されたオブジェクトの数です。
bbox.resize(0, 0);
IlUInt nbselections;
IlvGraphic** objs = manager->getSelections(nbselections);
次の部分は各オブジェクトをスキャンするループを開始します。
IlvRect rect;
for (IlUInt i=0; i < nbselections; i++) {
この次の部分では各オブジェクトのバウンディング・ボックスを呼び出してビュー座標系に変換し、結果に追加します。
objs[i]->boundingBox(rect, t);
for (IlUInt i=0; i < nbselections; i++) {
objs[i]->boundingBox(rect, t);
bbox.add(rect);
}
handleButtonDragged メンバー関数
移動中のオブジェクトがあり、それが可動である場合、ドラッグ位置はマネージャー・グリッドにスナップされ (存在する場合)、新しい _xor_rectangle が計算されます。次に、メンバー関数 ensureVisible によって、ユーザーがドラッグするポイントがビューの表示部分に確実に維持されます。
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 メンバー関数
移動するオブジェクトがあれば、メンバー関数 doIt を呼び出すことにより差分移動を行います。移動するオブジェクトがない場合は、最後に指定されたオブジェクトは選択解除されます。
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.0
Copyright © 2015, Rogue Wave Software, Inc. All Rights Reserved.