Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "tools/tool.h"
Toshihiro Shimizu 890ddd
#include "tools/toolutils.h"
Toshihiro Shimizu 890ddd
#include "tools/cursors.h"
Toshihiro Shimizu 890ddd
#include "tproperty.h"
Toshihiro Shimizu 890ddd
#include "tdata.h"
Toshihiro Shimizu 890ddd
#include "tconvert.h"
Toshihiro Shimizu 890ddd
#include "tgl.h"
Toshihiro Shimizu 890ddd
#include "tstroke.h"
Toshihiro Shimizu 890ddd
#include "tvectorimage.h"
Toshihiro Shimizu 890ddd
#include "hookselection.h"
Toshihiro Shimizu 890ddd
#include "tools/toolhandle.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "toonzqt/selection.h"
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include "toonz/tframehandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshlevelhandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/tscenehandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/txsheethandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/tcolumnhandle.h"
Toshihiro Shimizu 890ddd
#include "toonz/hook.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshlevel.h"
Toshihiro Shimizu 890ddd
#include "toonz/toonzscene.h"
Toshihiro Shimizu 890ddd
#include "toonz/txsheet.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshcell.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshcolumn.h"
Toshihiro Shimizu 890ddd
#include "toonz/tstageobjecttree.h"
Toshihiro Shimizu 890ddd
#include "toonz/txshsimplelevel.h"
Toshihiro Shimizu 890ddd
#include "toonz/levelproperties.h"
Toshihiro Shimizu 890ddd
#include "toonz/toonzimageutils.h"
Toshihiro Shimizu 890ddd
#include "toonz/dpiscale.h"
Toshihiro Shimizu 890ddd
#include "toonz/onionskinmask.h"
Toshihiro Shimizu 890ddd
#include "toonz/tonionskinmaskhandle.h"
Toshihiro Shimizu 890ddd
#include <math.h></math.h>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// For Qt translation support
Toshihiro Shimizu 890ddd
#include <qcoreapplication></qcoreapplication>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
#include <qpainter></qpainter>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
using namespace ToolUtils;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Shinya Kitaoka 120a6e
namespace {
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Toshihiro Shimizu 890ddd
// OtherHook
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class OtherHook {
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  int m_columnIndex;
Shinya Kitaoka 120a6e
  int m_hookIndex;
Shinya Kitaoka 120a6e
  TPointD m_hookPos;
Shinya Kitaoka 120a6e
  OtherHook(int columnIndex, int hookIndex, const TPointD &hookPos)
Shinya Kitaoka 120a6e
      : m_columnIndex(columnIndex)
Shinya Kitaoka 120a6e
      , m_hookIndex(hookIndex)
Shinya Kitaoka 120a6e
      , m_hookPos(hookPos) {}
Toshihiro Shimizu 890ddd
};
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
}  // namespace
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//=============================================================================
Toshihiro Shimizu 890ddd
// HookTool
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
class HookTool : public TTool {
Shinya Kitaoka 120a6e
  Q_DECLARE_TR_FUNCTIONS(HookTool)
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  HookSelection m_selection;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD m_firstPos, m_lastPos;
Shinya Kitaoka 120a6e
  int m_hookId, m_hookSide;
Shinya Kitaoka 120a6e
  bool m_deselectArmed;
Shinya Kitaoka 120a6e
  HookUndo *m_undo;
Shinya Kitaoka 120a6e
  std::vector<otherhook> m_otherHooks;</otherhook>
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPropertyGroup m_prop;
Shinya Kitaoka 120a6e
  TBoolProperty m_snappedActive;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  TPointD m_snappedPos;
Shinya Kitaoka 120a6e
  std::string m_snappedReason;
Shinya Kitaoka 120a6e
  TRectD m_shapeBBox;
Shinya Kitaoka 120a6e
  bool m_snapped, m_hookSetChanged;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  bool m_buttonDown;
Shinya Kitaoka 120a6e
  TPointD m_pivotOffset;
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
  void getOtherHooks(std::vector<otherhook> &otherHooks);</otherhook>
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
public:
Shinya Kitaoka 120a6e
  HookTool();
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  ToolType getToolType() const override { return TTool::LevelReadTool; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  HookSet *getHookSet() const;
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void updateTranslation() override;
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void draw() override;
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void leftButtonDown(const TPointD &pos, const TMouseEvent &e) override;
Shinya Kitaoka 473e70
  void leftButtonDrag(const TPointD &pos, const TMouseEvent &e) override;
Shinya Kitaoka 473e70
  void leftButtonUp(const TPointD &pos, const TMouseEvent &) override;
Shinya Kitaoka 473e70
  void mouseMove(const TPointD &pos, const TMouseEvent &e) override;
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  void onActivate() override;
Shinya Kitaoka 473e70
  void onDeactivate() override;
Shinya Kitaoka 473e70
  void onEnter() override;
Shinya Kitaoka 473e70
  void onImageChanged() override {
Shinya Kitaoka 120a6e
    m_selection.selectNone();
Shinya Kitaoka 120a6e
    m_hookId = -1;
Shinya Kitaoka 120a6e
    m_otherHooks.clear();
Shinya Kitaoka 120a6e
    getOtherHooks(m_otherHooks);
Shinya Kitaoka 120a6e
    invalidate();
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  TPropertyGroup *getProperties(int targetType) override { return &m_prop; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 473e70
  int getCursorId() const override { return ToolCursor::MoveCursor; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  void onSelectionChanged() { invalidate(); }
Shinya Kitaoka 120a6e
  bool select(const TSelection *) { return false; }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool pick(int &hookId, int &side, const TPointD &pos);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool snap(TPointD &pos, double &range2);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool isSnappedActive() const { return m_snappedActive.getValue(); }
Shinya Kitaoka 120a6e
  bool isEditingScene() const {
Shinya Kitaoka 120a6e
    return getApplication()->getCurrentFrame()->isEditingScene();
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // pivot is the hook the level is attached to.
Shinya Kitaoka 120a6e
  // returns -1 if no pivot is defined.
Shinya Kitaoka 120a6e
  // Hook1 <==> index=0
Shinya Kitaoka 120a6e
  int getPivotIndex() {
Shinya Kitaoka 120a6e
    if (!isEditingScene())
Shinya Kitaoka 120a6e
      return -1;  // a pivot can be defined only when editing a scene
Shinya Kitaoka 120a6e
    std::string handle =
Shinya Kitaoka 120a6e
        getXsheet()->getStageObject(getObjectId())->getHandle();
Shinya Kitaoka 120a6e
    if (handle.find("H") != 0) return -1;
Shinya Kitaoka 120a6e
    return std::stoi(handle.substr(1)) - 1;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  void drawHooks(HookSet *hookSet, const TFrameId &fid, bool isOnion);
Shinya Kitaoka 120a6e
  // other hooks are drawn in the current level reference frame
Shinya Kitaoka 120a6e
  // when the current matrix changes (e.g. because of a parent change or a
Shinya Kitaoka 120a6e
  // handle change)
Shinya Kitaoka 120a6e
  // we must recompute the hooks positions
Shinya Kitaoka 473e70
  void updateMatrix() override {
Shinya Kitaoka 120a6e
    TTool::updateMatrix();
Shinya Kitaoka 120a6e
    m_otherHooks.clear();
Shinya Kitaoka 120a6e
    getOtherHooks(m_otherHooks);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
} hookTool;
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
HookTool::HookTool()
Shinya Kitaoka 120a6e
    : TTool("T_Hook")
Shinya Kitaoka 120a6e
    , m_hookId(-1)
Shinya Kitaoka 120a6e
    , m_hookSide(0)
Shinya Kitaoka 120a6e
    , m_deselectArmed(false)
Shinya Kitaoka 120a6e
    , m_undo(0)
Shinya Kitaoka 120a6e
    , m_snappedActive("Snap", true)  // W_ToolOptions_Snapped
Shinya Kitaoka 120a6e
    , m_snappedPos()
Shinya Kitaoka 120a6e
    , m_snappedReason("")
Shinya Kitaoka 120a6e
    , m_shapeBBox()
Shinya Kitaoka 120a6e
    , m_snapped(false)
Shinya Kitaoka 120a6e
    , m_hookSetChanged(false)
Shinya Kitaoka 120a6e
    , m_buttonDown(false)
Shinya Kitaoka 120a6e
    , m_pivotOffset() {
Shinya Kitaoka 120a6e
  bind(TTool::CommonLevels);
Shinya Kitaoka 120a6e
  m_prop.bind(m_snappedActive);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_snappedActive.setId("Snap");
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::updateTranslation() {
Shinya Kitaoka 120a6e
  m_snappedActive.setQStringName(tr("Snap"));
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
HookSet *HookTool::getHookSet() const {
Shinya Kitaoka 120a6e
  TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
Shinya Kitaoka 120a6e
  if (!xl) return 0;
Shinya Kitaoka 120a6e
  return xl->getHookSet();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//
Toshihiro Shimizu 890ddd
// search for hooks located on other columns (they are used as a reference)
Toshihiro Shimizu 890ddd
// the position of hooks is computed in the current level reference frame
Toshihiro Shimizu 890ddd
//
Shinya Kitaoka 120a6e
void HookTool::getOtherHooks(std::vector<otherhook> &otherHooks) {</otherhook>
Shinya Kitaoka 120a6e
  if (!getViewer()) return;
Shinya Kitaoka 120a6e
  TPointD dpi      = getViewer()->getDpiScale();
Shinya Kitaoka 120a6e
  TAffine myAffInv = (getCurrentColumnMatrix() * TScale(dpi.x, dpi.y)).inv();
Shinya Kitaoka 120a6e
  TXsheet *xsh     = getXsheet();
Shinya Kitaoka 120a6e
  int row          = getFrame();
Shinya Kitaoka 120a6e
  int curCol       = getColumnIndex();
Shinya Kitaoka 120a6e
  int i;
Shinya Kitaoka 120a6e
  for (i = 0; i < xsh->getColumnCount(); i++) {
Shinya Kitaoka 120a6e
    if (!xsh->getColumn(i) || !xsh->getColumn(i)->isCamstandVisible()) continue;
Shinya Kitaoka 120a6e
    if (i == curCol) continue;
Shinya Kitaoka 120a6e
    TXshCell cell = xsh->getCell(row, i);
Shinya Kitaoka 120a6e
    if (cell.isEmpty()) continue;
Shinya Kitaoka 120a6e
    TAffine aff =
Shinya Kitaoka 120a6e
        myAffInv * xsh->getPlacement(TStageObjectId::ColumnId(i), row);
Shinya Kitaoka 120a6e
    if (cell.getSimpleLevel())
Shinya Kitaoka 120a6e
      aff = aff * getDpiAffine(cell.getSimpleLevel(), cell.m_frameId, true);
Shinya Kitaoka 120a6e
    HookSet *hookSet = cell.m_level->getHookSet();
Shinya Kitaoka 120a6e
    int j;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (j = 0; j < hookSet->getHookCount(); j++) {
Shinya Kitaoka 120a6e
      Hook *hook = hookSet->getHook(j);
Shinya Kitaoka 120a6e
      if (hook == 0 || hook->isEmpty()) continue;
Shinya Kitaoka 120a6e
      TPointD aPos = aff * hook->getAPos(cell.m_frameId);
Shinya Kitaoka 120a6e
      TPointD bPos = aff * hook->getBPos(cell.m_frameId);
Shinya Kitaoka 120a6e
      otherHooks.push_back(OtherHook(i, j, aPos));
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::drawHooks(HookSet *hookSet, const TFrameId &fid, bool isOnion) {
Shinya Kitaoka 120a6e
  int pivotIndex = getPivotIndex();
Shinya Kitaoka 120a6e
  int i;
Shinya Kitaoka 120a6e
  for (i = 0; i < hookSet->getHookCount(); i++) {
Shinya Kitaoka 120a6e
    Hook *hook = hookSet->getHook(i);
Shinya Kitaoka 120a6e
    if (!hook || hook->isEmpty()) continue;
Shinya Kitaoka 120a6e
    assert(hook);
Shinya Kitaoka 120a6e
    TPointD p0 = hook->getAPos(fid);
Shinya Kitaoka 120a6e
    TPointD p1 = hook->getBPos(fid);
Shinya Kitaoka 120a6e
    if (pivotIndex == i) {
Shinya Kitaoka 120a6e
      // translating the pivot doesn't change its actual position, but just the
Shinya Kitaoka 120a6e
      // m_pivotOffset
Shinya Kitaoka 120a6e
      // the actual pivot position is changed in the mouseUp event
Shinya Kitaoka 120a6e
      p0 += m_pivotOffset;
Shinya Kitaoka 120a6e
      p1 += m_pivotOffset;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    bool linked = p0 == p1;
Shinya Kitaoka 120a6e
    drawHook(p0, linked ? NormalHook : PassHookA, m_selection.isSelected(i, 1),
Shinya Kitaoka 120a6e
             isOnion);
Shinya Kitaoka 120a6e
    if (!linked) drawHook(p1, PassHookB, m_selection.isSelected(i, 2), isOnion);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::draw() {
Shinya Kitaoka 120a6e
  // ToolUtils::drawRect(TRectD(10,10,110,110), TPixel32(255,200,200), 0xFFF0);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw the current image bounding box
Shinya Kitaoka 120a6e
  const double v200 = 200.0 / 255.0;
Shinya Kitaoka 120a6e
  TImageP image     = getImage(false);
Shinya Kitaoka 120a6e
  if (!image) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TToonzImageP ti  = image;
Shinya Kitaoka 120a6e
  TVectorImageP vi = image;
Shinya Kitaoka 120a6e
  if (ti) {
Shinya Kitaoka 120a6e
    TRectD bbox =
Shinya Kitaoka 120a6e
        ToonzImageUtils::convertRasterToWorld(convert(ti->getBBox()), ti);
Shinya Kitaoka 120a6e
    ToolUtils::drawRect(bbox * ti->getSubsampling(), TPixel32(200, 200, 200),
Shinya Kitaoka 120a6e
                        0x5555);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (vi) {
Shinya Kitaoka 120a6e
    TRectD bbox = vi->getBBox();
Shinya Kitaoka 120a6e
    ToolUtils::drawRect(bbox, TPixel32(200, 200, 200), 0x5555);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw hooks
Shinya Kitaoka 120a6e
  HookSet *hookSet = getHookSet();
Shinya Kitaoka 120a6e
  if (!hookSet) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TTool::Application *app = TTool::getApplication();
Shinya Kitaoka 120a6e
  TFrameHandle *fh        = app->getCurrentFrame();
Shinya Kitaoka 120a6e
  TFrameId fid            = getCurrentFid();
Shinya Kitaoka 120a6e
  TXshSimpleLevel *level  = 0;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  OnionSkinMask osm = app->getCurrentOnionSkin()->getOnionSkinMask();
Shinya Kitaoka 120a6e
  std::vector<int> os;</int>
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (isEditingScene())
Shinya Kitaoka 120a6e
    osm.getAll(getFrame(), os);
Shinya Kitaoka 120a6e
  else {
Shinya Kitaoka 120a6e
    level = app->getCurrentLevel()->getSimpleLevel();
Shinya Kitaoka 120a6e
    assert(level);
Shinya Kitaoka 120a6e
    osm.getAll(level->guessIndex(fid), os);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int i;
Shinya Kitaoka 120a6e
  if (osm.isEnabled())
Shinya Kitaoka 120a6e
    for (i = 0; i < (int)os.size(); i++) {
Shinya Kitaoka 120a6e
      if (isEditingScene()) {
Shinya Kitaoka 120a6e
        const TXshCell &cell = getXsheet()->getCell(
Shinya Kitaoka 120a6e
            os[i], app->getCurrentColumn()->getColumnIndex());
Shinya Kitaoka 120a6e
        drawHooks(hookSet, cell.getFrameId(), true);
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        const TFrameId &fid2 = level->index2fid(os[i]);
Shinya Kitaoka 120a6e
        drawHooks(hookSet, fid2, true);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  drawHooks(hookSet, fid, false);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // TXshCell cell = xsh->getCell(row, i);
Shinya Kitaoka 120a6e
  // draw other level hooks
Shinya Kitaoka 120a6e
  if (isSnappedActive() && isEditingScene()) {
Shinya Kitaoka 120a6e
    for (i = 0; i < (int)m_otherHooks.size(); i++) {
Shinya Kitaoka 120a6e
      drawHook(m_otherHooks[i].m_hookPos, OtherLevelHook, false);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // draw hooks balloons
Shinya Kitaoka 120a6e
  std::vector<trectd> balloons;  // this is used to avoid balloons overlapping</trectd>
Shinya Kitaoka 120a6e
  int pivotIndex = getPivotIndex();
Shinya Kitaoka 120a6e
  for (i = 0; i < hookSet->getHookCount(); i++) {
Shinya Kitaoka 120a6e
    Hook *hook = hookSet->getHook(i);
Shinya Kitaoka 120a6e
    if (!hook || hook->isEmpty()) continue;
Shinya Kitaoka 120a6e
    assert(hook);
Shinya Kitaoka 120a6e
    TPointD p0  = hook->getAPos(fid);
Shinya Kitaoka 120a6e
    TPointD p1  = hook->getBPos(fid);
Shinya Kitaoka 120a6e
    bool linked = p0 == p1;
Shinya Kitaoka 120a6e
    if (i == pivotIndex) {
Shinya Kitaoka 120a6e
      p0 += m_pivotOffset;
Shinya Kitaoka 120a6e
      p1 += m_pivotOffset;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    TPixel32 balloonColor(200, 220, 205, 200);
Shinya Kitaoka 120a6e
    TPoint balloonOffset(20, 20);
Shinya Kitaoka 120a6e
    std::string hookName = std::to_string(i + 1);
Shinya Kitaoka 120a6e
    drawBalloon(p0, hookName, balloonColor, balloonOffset, false, &balloons);
Shinya Kitaoka 120a6e
    if (!linked)
Shinya Kitaoka 120a6e
      drawBalloon(p1, hookName, balloonColor, balloonOffset, false, &balloons);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  // draw snapped hook balloon
Shinya Kitaoka 120a6e
  if (m_snappedReason != "") {
Shinya Kitaoka 120a6e
    TPointD pos = m_snappedPos;
Shinya Kitaoka 120a6e
    TRectD bbox = m_shapeBBox;
Shinya Kitaoka 120a6e
    if (bbox.getLx() > 0 && bbox.getLy() > 0) {
Shinya Kitaoka 120a6e
      glColor3d(v200, v200, v200);
Shinya Kitaoka 120a6e
      glEnable(GL_LINE_STIPPLE);
Shinya Kitaoka 120a6e
      glLineStipple(5, 0xAAAA);
Shinya Kitaoka 120a6e
      tglDrawRect(bbox);
Shinya Kitaoka 120a6e
      glDisable(GL_LINE_STIPPLE);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
      glBegin(GL_LINES);
Shinya Kitaoka 120a6e
      glVertex2d(pos.x, bbox.y0);
Shinya Kitaoka 120a6e
      glVertex2d(pos.x, bbox.y1);
Shinya Kitaoka 120a6e
      glVertex2d(bbox.x0, pos.y);
Shinya Kitaoka 120a6e
      glVertex2d(bbox.x1, pos.y);
Shinya Kitaoka 120a6e
      glEnd();
Shinya Kitaoka 120a6e
      glDisable(GL_LINE_STIPPLE);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    drawBalloon(pos, m_snappedReason, TPixel32(200, 250, 180, 200),
Shinya Kitaoka 120a6e
                TPoint(20, 20), false, &balloons);
Shinya Kitaoka 120a6e
  }
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
// side : 1=A,2=B,3=A&B
Shinya Kitaoka 120a6e
bool HookTool::pick(int &hookId, int &side, const TPointD &pos) {
Shinya Kitaoka 120a6e
  HookSet *hookSet = getHookSet();
Shinya Kitaoka 120a6e
  if (!hookSet) return false;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TFrameId fid    = getCurrentFid();
Shinya Kitaoka 120a6e
  double minDist2 = 1e8;
Shinya Kitaoka 120a6e
  for (int i = 0; i < hookSet->getHookCount(); i++) {
Shinya Kitaoka 120a6e
    Hook *hook = hookSet->getHook(i);
Shinya Kitaoka 120a6e
    if (!hook || hook->isEmpty()) continue;
Shinya Kitaoka 120a6e
    TPointD aPos = hook->getAPos(fid);
Shinya Kitaoka 120a6e
    TPointD bPos = hook->getBPos(fid);
Shinya Kitaoka 120a6e
    bool linked  = aPos == bPos;
Shinya Kitaoka 120a6e
    if (linked) {
Shinya Kitaoka 120a6e
      double dist2 = tdistance2(pos, aPos);
Shinya Kitaoka 120a6e
      if (dist2 < minDist2) {
Shinya Kitaoka 120a6e
        minDist2 = dist2;
Shinya Kitaoka 120a6e
        hookId   = hook->getId();
Shinya Kitaoka 120a6e
        side     = 3;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      double aDist2 = tdistance2(pos, aPos);
Shinya Kitaoka 120a6e
      double bDist2 = tdistance2(pos, bPos);
Shinya Kitaoka 120a6e
      double dist2  = aDist2;
Shinya Kitaoka 120a6e
      int s         = 1;
Shinya Kitaoka 120a6e
      if (bDist2 < dist2) {
Shinya Kitaoka 120a6e
        dist2 = bDist2;
Shinya Kitaoka 120a6e
        s     = 2;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
      if (dist2 < minDist2) {
Shinya Kitaoka 120a6e
        minDist2 = dist2;
Shinya Kitaoka 120a6e
        hookId   = hook->getId();
Shinya Kitaoka 120a6e
        side     = s;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  double pixelSize = getPixelSize();
Shinya Kitaoka 120a6e
  return minDist2 < 100 * pixelSize * pixelSize;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e) {
Shinya Kitaoka 120a6e
  TTool::Application *app = TTool::getApplication();
Shinya Kitaoka 120a6e
  if (!app) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_buttonDown = true;
Shinya Kitaoka 120a6e
  m_snapped    = false;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TXshLevel *xl                          = app->getCurrentLevel()->getLevel();
Shinya Kitaoka 120a6e
  if (xl && xl->getSimpleLevel()) m_undo = new HookUndo(xl->getSimpleLevel());
Shinya Kitaoka 120a6e
  m_selection.setLevel(xl);
Shinya Kitaoka 120a6e
  m_firstPos = m_lastPos = pos;
Shinya Kitaoka 120a6e
  m_hookId = -1, m_hookSide = 0;
Shinya Kitaoka 120a6e
  m_deselectArmed = false;
Shinya Kitaoka 120a6e
  if (pick(m_hookId, m_hookSide, pos)) {
Shinya Kitaoka 120a6e
    if (m_hookSide == 3)  // ho cliccato su un cerchio-croce
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      if (e.isAltPressed())  // con l'alt voglio dividere
Shinya Kitaoka 120a6e
      {
Shinya Kitaoka 120a6e
        m_selection.selectNone();
Shinya Kitaoka 120a6e
        m_selection.select(m_hookId, 2);
Shinya Kitaoka 120a6e
      } else if (e.isCtrlPressed()) {
Shinya Kitaoka 120a6e
        // se sono tutti e due selezionati li deseleziono
Shinya Kitaoka 120a6e
        if (m_selection.isSelected(m_hookId, 1) &&
Shinya Kitaoka 120a6e
            m_selection.isSelected(m_hookId, 2)) {
Shinya Kitaoka 120a6e
          m_selection.unselect(m_hookId, 1);
Shinya Kitaoka 120a6e
          m_selection.unselect(m_hookId, 2);
Shinya Kitaoka 120a6e
        } else  // altrimenti li seleziono tutti e due
Shinya Kitaoka 120a6e
        {
Shinya Kitaoka 120a6e
          m_selection.select(m_hookId, 1);
Shinya Kitaoka 120a6e
          m_selection.select(m_hookId, 2);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      } else {
Shinya Kitaoka 120a6e
        // senza control ne' shift ne' alt: se ho cliccato su un hook non
Shinya Kitaoka 120a6e
        // selezionato
Shinya Kitaoka 120a6e
        // deseleziono tutti gli altri e lo seleziono, altrimenti lascio tutto
Shinya Kitaoka 120a6e
        // com'e' e al buttonup se non c'e' stato movimento deseleziono gli
Shinya Kitaoka 120a6e
        // altri
Shinya Kitaoka 120a6e
        if (m_selection.isSelected(m_hookId, 1) ||
Shinya Kitaoka 120a6e
            m_selection.isSelected(m_hookId, 2))
Shinya Kitaoka 120a6e
          m_deselectArmed = true;
Shinya Kitaoka 120a6e
        else {
Shinya Kitaoka 120a6e
          m_selection.selectNone();
Shinya Kitaoka 120a6e
          m_selection.select(m_hookId, 1);
Shinya Kitaoka 120a6e
          m_selection.select(m_hookId, 2);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    } else  // ho cliccato o su un cerchio o su una croce
Shinya Kitaoka 120a6e
    {
Shinya Kitaoka 120a6e
      if (e.isCtrlPressed())
Shinya Kitaoka 120a6e
        m_selection.invertSelection(m_hookId, m_hookSide);
Shinya Kitaoka 120a6e
      else {
Shinya Kitaoka 120a6e
        m_selection.selectNone();
Shinya Kitaoka 120a6e
        m_selection.select(m_hookId, m_hookSide);
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } else {
Shinya Kitaoka 120a6e
    // non ho cliccato su nulla: con ctrl non faccio nulla, senza creo un nuovo
Shinya Kitaoka 120a6e
    // hook
Shinya Kitaoka 120a6e
    if (!e.isCtrlPressed()) {
Shinya Kitaoka 120a6e
      m_selection.selectNone();
Shinya Kitaoka 120a6e
      TFrameId fid     = getCurrentFid();
Shinya Kitaoka 120a6e
      HookSet *hookSet = getHookSet();
Shinya Kitaoka 120a6e
      if (hookSet && xl->getSimpleLevel() &&
Shinya Kitaoka 120a6e
          !xl->getSimpleLevel()->isReadOnly()) {
Shinya Kitaoka 120a6e
        Hook *hook       = hookSet->addHook();
Shinya Kitaoka 120a6e
        m_hookSetChanged = true;
Shinya Kitaoka 120a6e
        if (hook) {
Shinya Kitaoka 120a6e
          TPointD ppos(pos);
Shinya Kitaoka 120a6e
          if (m_snappedReason != "") {
Shinya Kitaoka 120a6e
            m_snapped = true;
Shinya Kitaoka 120a6e
            ppos      = m_snappedPos;
Shinya Kitaoka 120a6e
          }
Shinya Kitaoka 120a6e
          m_snappedReason = "";
Shinya Kitaoka 120a6e
          hook->setAPos(fid, ppos);
Shinya Kitaoka 120a6e
          ppos = hook->getAPos(fid);
Shinya Kitaoka 120a6e
          m_selection.select(hook->getId(), 1);
Shinya Kitaoka 120a6e
          m_selection.select(hook->getId(), 2);
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  m_pivotOffset = TPointD();
Shinya Kitaoka 120a6e
  m_selection.makeCurrent();
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::leftButtonDrag(const TPointD &pp, const TMouseEvent &e) {
Shinya Kitaoka 120a6e
  TTool::Application *app = TTool::getApplication();
Shinya Kitaoka 120a6e
  if (!app) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_snapped) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TFrameId fid = getCurrentFid();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // cerco di capire se sto muovendo un unico hook "unito"
Shinya Kitaoka 120a6e
  TPointD hookPos;
Shinya Kitaoka 120a6e
  HookSet *hookSet = getHookSet();
Shinya Kitaoka 120a6e
  if (!hookSet) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  bool draggingPivot = false;
Shinya Kitaoka 120a6e
  int pivotIndex     = getPivotIndex();
Shinya Kitaoka 120a6e
  int i, count = 0;
Shinya Kitaoka 120a6e
  for (i = 0; i < hookSet->getHookCount(); i++) {
Shinya Kitaoka 120a6e
    Hook *hook = hookSet->getHook(i);
Shinya Kitaoka 120a6e
    if (!hook || hook->isEmpty()) continue;
Shinya Kitaoka 120a6e
    TPointD aPos = hook->getAPos(fid);
Shinya Kitaoka 120a6e
    TPointD bPos = hook->getBPos(fid);
Shinya Kitaoka 120a6e
    if (m_selection.isSelected(i, 1) && m_selection.isSelected(i, 2) &&
Shinya Kitaoka 120a6e
        aPos == bPos) {
Shinya Kitaoka 120a6e
      count++;
Shinya Kitaoka 120a6e
      hookPos = aPos;
Shinya Kitaoka 120a6e
    } else if (m_selection.isSelected(i, 1) || m_selection.isSelected(i, 2)) {
Shinya Kitaoka 120a6e
      count = 0;
Shinya Kitaoka 120a6e
      break;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    if (i == pivotIndex && m_selection.isSelected(i, 1)) draggingPivot = true;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TPointD pos(pp);
Shinya Kitaoka 120a6e
  TPointD delta = pos - m_lastPos;
Shinya Kitaoka 120a6e
  if (e.isShiftPressed()) {
Shinya Kitaoka 120a6e
    // se e' pigiato lo shift non snappa e si muove solo in
Shinya Kitaoka 120a6e
    // orizzontale/verticale
Shinya Kitaoka 120a6e
    TPointD d = pos - m_firstPos;
Shinya Kitaoka 120a6e
    if (d.x * d.x > d.y * d.y)
Shinya Kitaoka 120a6e
      pos.y = m_firstPos.y;
Shinya Kitaoka 120a6e
    else
Shinya Kitaoka 120a6e
      pos.x = m_firstPos.x;
Shinya Kitaoka 120a6e
    delta   = pos - m_lastPos;
Shinya Kitaoka 120a6e
  } else if (count == 1 && isSnappedActive() && !draggingPivot) {
Shinya Kitaoka 120a6e
    // snappa
Shinya Kitaoka 120a6e
    TPointD oldHookPos = hookPos;
Shinya Kitaoka 120a6e
    hookPos += pos - m_lastPos;
Shinya Kitaoka 120a6e
    double range2 = getPixelSize() * 20;
Shinya Kitaoka 120a6e
    range2 *= range2;
Shinya Kitaoka 120a6e
    m_snappedReason = "";
Shinya Kitaoka 120a6e
    m_shapeBBox     = TRectD();
Shinya Kitaoka 120a6e
    if (snap(hookPos, range2)) {
Shinya Kitaoka 120a6e
      delta = hookPos - oldHookPos;
Shinya Kitaoka 120a6e
      pos   = delta + m_lastPos;
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  } else if (count > 1 && isSnappedActive() && !draggingPivot) {
Shinya Kitaoka 120a6e
    TPointD oldHookPos = hookPos;
Shinya Kitaoka 120a6e
    // delta = pos - m_lastPos;
Shinya Kitaoka 120a6e
    TPointD snappedDelta = delta;
Shinya Kitaoka 120a6e
    double range2        = getPixelSize() * 20;
Shinya Kitaoka 120a6e
    range2 *= range2;
Shinya Kitaoka 120a6e
    m_snappedReason = "";
Shinya Kitaoka 120a6e
    m_shapeBBox     = TRectD();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    for (i = 0; i < hookSet->getHookCount(); i++) {
Shinya Kitaoka 120a6e
      Hook *hook = hookSet->getHook(i);
Shinya Kitaoka 120a6e
      if (hook && !hook->isEmpty()) {
Shinya Kitaoka 120a6e
        if (m_selection.isSelected(i, 1)) {
Shinya Kitaoka 120a6e
          TPointD p0 = hook->getAPos(fid);
Shinya Kitaoka 120a6e
          TPointD p1 = p0 + delta;
Shinya Kitaoka 120a6e
          if (snap(p1, range2)) snappedDelta = p1 - p0;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
        if (m_selection.isSelected(i, 1)) {
Shinya Kitaoka 120a6e
          TPointD p0 = hook->getBPos(fid);
Shinya Kitaoka 120a6e
          TPointD p1 = p0 + delta;
Shinya Kitaoka 120a6e
          if (snap(p1, range2)) snappedDelta = p1 - p0;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    pos   = m_lastPos + snappedDelta;
Shinya Kitaoka 120a6e
    delta = snappedDelta;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_lastPos = pos;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  TXsheet *xsh = getXsheet();
Shinya Kitaoka 120a6e
  //  std::string handle =
Shinya Kitaoka 120a6e
  //  getXsheet()->getStageObject(TStageObjectId::ColumnId())->getHandle();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // actual movement
Shinya Kitaoka 120a6e
  for (i = 0; i < hookSet->getHookCount(); i++) {
Shinya Kitaoka 120a6e
    Hook *hook = hookSet->getHook(i);
Shinya Kitaoka 120a6e
    if (!hook || hook->isEmpty()) continue;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
    TPointD aPos   = hook->getAPos(fid);
Shinya Kitaoka 120a6e
    TPointD bPos   = hook->getBPos(fid);
Shinya Kitaoka 120a6e
    bool aSelected = m_selection.isSelected(i, 1);
Shinya Kitaoka 120a6e
    bool bSelected = m_selection.isSelected(i, 2);
Shinya Kitaoka 120a6e
    if (pivotIndex == i) {
Shinya Kitaoka 120a6e
      if (aSelected) {
Shinya Kitaoka 120a6e
        m_pivotOffset += delta;
Shinya Kitaoka 120a6e
        if (!bSelected) hook->setBPos(fid, bPos - delta);
Shinya Kitaoka 120a6e
      } else if (bSelected)
Shinya Kitaoka 120a6e
        hook->setBPos(fid, bPos + delta);
Shinya Kitaoka 120a6e
    } else {
Shinya Kitaoka 120a6e
      if (aSelected) hook->setAPos(fid, aPos + delta);
Shinya Kitaoka 120a6e
      if (bSelected) hook->setBPos(fid, bPos + delta);
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
    m_hookSetChanged = true;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  getXsheet()->getStageObjectTree()->invalidateAll();
Shinya Kitaoka 120a6e
  invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::leftButtonUp(const TPointD &pos, const TMouseEvent &) {
Shinya Kitaoka 120a6e
  TTool::Application *app = TTool::getApplication();
Shinya Kitaoka 120a6e
  if (!app) return;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // note: apparently sometimes (when the user triple-clicks) we receive this
Shinya Kitaoka 120a6e
  // event twice
Shinya Kitaoka 120a6e
  if (!m_buttonDown) return;
Shinya Kitaoka 120a6e
  m_buttonDown = false;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  // if I've moved the pivot hook I have to perform the actual movement in the
Shinya Kitaoka 120a6e
  // mouse up event
Shinya Kitaoka 120a6e
  int pivotIndex = getPivotIndex();
Shinya Kitaoka 120a6e
  if (m_selection.isSelected(pivotIndex, 1) && m_pivotOffset != TPointD()) {
Shinya Kitaoka 120a6e
    HookSet *hookSet = getHookSet();
Shinya Kitaoka 120a6e
    if (hookSet) {
Shinya Kitaoka 120a6e
      Hook *hook = hookSet->getHook(pivotIndex);
Shinya Kitaoka 120a6e
      if (hook && !hook->isEmpty()) {
Shinya Kitaoka 120a6e
        TFrameId fid = getCurrentFid();
Shinya Kitaoka 120a6e
        TPointD aPos = hook->getAPos(fid);
Shinya Kitaoka 120a6e
        TPointD bPos = hook->getBPos(fid);
Shinya Kitaoka 120a6e
        hook->setAPos(fid, aPos + m_pivotOffset);
Shinya Kitaoka 120a6e
        hook->setBPos(fid, bPos + m_pivotOffset);
Shinya Kitaoka 120a6e
        getXsheet()->getStageObjectTree()->invalidateAll();
Shinya Kitaoka 120a6e
        updateMatrix();
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_snapped = false;
Shinya Kitaoka 120a6e
  // m_selection.selectNone();
Shinya Kitaoka 120a6e
  TXshLevel *xl = app->getCurrentLevel()->getLevel();
Shinya Kitaoka 120a6e
  if (!xl || !xl->getSimpleLevel()) return;
Shinya Kitaoka 120a6e
  xl->getSimpleLevel()->getProperties()->setDirtyFlag(true);
Shinya Kitaoka 120a6e
  TPointD delta = m_lastPos - m_firstPos;
Shinya Kitaoka 120a6e
  if (m_deselectArmed && norm2(delta) < 10) {
Shinya Kitaoka 120a6e
    m_selection.selectNone();
Shinya Kitaoka 120a6e
    m_selection.unselect(m_hookId, 1);
Shinya Kitaoka 120a6e
    m_selection.unselect(m_hookId, 2);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  m_deselectArmed = false;
Shinya Kitaoka 120a6e
  if (m_undo && m_hookSetChanged)
Shinya Kitaoka 120a6e
    TUndoManager::manager()->add(m_undo);
Shinya Kitaoka 120a6e
  else {
Shinya Kitaoka 120a6e
    delete m_undo;
Shinya Kitaoka 120a6e
    m_undo = 0;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  m_hookSetChanged = false;
Shinya Kitaoka 120a6e
  m_pivotOffset    = TPointD();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
bool HookTool::snap(TPointD &pos, double &range2) {
Shinya Kitaoka 120a6e
  bool ret               = false;
Shinya Kitaoka 120a6e
  TPointD snappedPos     = pos;
Shinya Kitaoka 120a6e
  TVectorImageP vi       = TImageP(getImage(false));
Shinya Kitaoka 120a6e
  TStroke *selectedShape = 0;
Shinya Kitaoka 120a6e
  TRectD selectedShapeBBox;
Shinya Kitaoka 120a6e
  double selectedShapeBBoxArea = 0;
Shinya Kitaoka 120a6e
  int i, n;
Shinya Kitaoka 120a6e
  n = vi ? (int)vi->getStrokeCount() : 0;
Shinya Kitaoka 120a6e
  for (i = 0; i < n; i++) {
Shinya Kitaoka 120a6e
    TStroke *stroke = vi->getStroke(i);
Shinya Kitaoka 120a6e
    if (!stroke->isSelfLoop()) continue;
Shinya Kitaoka 120a6e
    TRectD bbox          = stroke->getBBox();
Shinya Kitaoka 120a6e
    TPointD strokeCenter = 0.5 * (bbox.getP00() + bbox.getP11());
Shinya Kitaoka 120a6e
    if (bbox.contains(pos)) {
Shinya Kitaoka 120a6e
      double dist2 = norm2(pos - strokeCenter);
Shinya Kitaoka 120a6e
      if (dist2 < range2) {
Shinya Kitaoka 120a6e
        double bboxArea = bbox.getLx() * bbox.getLy();
Shinya Kitaoka 120a6e
        if (selectedShape == 0 || selectedShapeBBoxArea > bboxArea) {
Shinya Kitaoka 120a6e
          range2                = dist2;
Shinya Kitaoka 120a6e
          selectedShape         = stroke;
Shinya Kitaoka 120a6e
          selectedShapeBBox     = bbox;
Shinya Kitaoka 120a6e
          selectedShapeBBoxArea = bboxArea;
Shinya Kitaoka 120a6e
        }
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (selectedShape) {
Shinya Kitaoka 120a6e
    m_shapeBBox = selectedShapeBBox;
Shinya Kitaoka 120a6e
    snappedPos =
Shinya Kitaoka 120a6e
        0.5 * (selectedShapeBBox.getP00() + selectedShapeBBox.getP11());
Shinya Kitaoka 120a6e
    m_snappedPos    = snappedPos;
Shinya Kitaoka 120a6e
    m_snappedReason = "shape center";
Shinya Kitaoka 120a6e
    ret             = true;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int k = -1;
Shinya Kitaoka 120a6e
  if (isEditingScene()) {
Shinya Kitaoka 120a6e
    for (i = 0; i < (int)m_otherHooks.size(); i++) {
Shinya Kitaoka 120a6e
      double dist2 = norm2(pos - m_otherHooks[i].m_hookPos);
Shinya Kitaoka 120a6e
      if (dist2 < range2) {
Shinya Kitaoka 120a6e
        range2 = dist2;
Shinya Kitaoka 120a6e
        k      = i;
Shinya Kitaoka 120a6e
      }
Shinya Kitaoka 120a6e
    }
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  if (k >= 0) {
Shinya Kitaoka 120a6e
    m_shapeBBox     = TRectD();
Shinya Kitaoka 120a6e
    snappedPos      = m_otherHooks[k].m_hookPos;
Shinya Kitaoka 120a6e
    m_snappedPos    = snappedPos;
Shinya Kitaoka 120a6e
    m_snappedReason = "Col" +
Shinya Kitaoka 120a6e
                      std::to_string(m_otherHooks[k].m_columnIndex + 1) + "/" +
Shinya Kitaoka 120a6e
                      std::to_string(m_otherHooks[k].m_hookIndex + 1);
Shinya Kitaoka 120a6e
    ret = true;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
  pos = snappedPos;
Shinya Kitaoka 120a6e
  return ret;
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::mouseMove(const TPointD &pos, const TMouseEvent &e) {
Shinya Kitaoka 120a6e
  std::string oldReason = m_snappedReason;
Shinya Kitaoka 120a6e
  TPointD oldPos        = m_snappedPos;
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_snappedPos    = TPointD();
Shinya Kitaoka 120a6e
  m_snappedReason = "";
Shinya Kitaoka 120a6e
  m_shapeBBox     = TRectD();
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  m_otherHooks.clear();
Shinya Kitaoka 120a6e
  getOtherHooks(m_otherHooks);
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  int hookId, side;
Shinya Kitaoka 120a6e
  if (pick(hookId, side, pos)) {
Shinya Kitaoka 120a6e
    if (oldReason != "") invalidate();
Shinya Kitaoka 120a6e
    return;
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (isSnappedActive()) {
Shinya Kitaoka 120a6e
    double range2 = getPixelSize() * 20.0;
Shinya Kitaoka 120a6e
    range2 *= range2;
Shinya Kitaoka 120a6e
    TPointD snappedPos = pos;
Shinya Kitaoka 120a6e
    snap(snappedPos, range2);
Shinya Kitaoka 120a6e
  }
Shinya Kitaoka 120a6e
Shinya Kitaoka 120a6e
  if (m_snappedReason != oldReason || m_snappedPos != oldPos) invalidate();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::onActivate() {
Shinya Kitaoka 120a6e
  // TODO: getApplication()->editImageOrSpline();
Shinya Kitaoka 120a6e
  m_otherHooks.clear();
Shinya Kitaoka 120a6e
  getOtherHooks(m_otherHooks);
Shinya Kitaoka 120a6e
  m_selection.makeCurrent();
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::onDeactivate() {
Shinya Kitaoka 120a6e
  m_selection.selectNone();
Shinya Kitaoka 120a6e
  TSelection::setCurrent(0);
Toshihiro Shimizu 890ddd
}
Toshihiro Shimizu 890ddd
Toshihiro Shimizu 890ddd
//-----------------------------------------------------------------------------
Toshihiro Shimizu 890ddd
Shinya Kitaoka 120a6e
void HookTool::onEnter() { m_selection.makeCurrent(); }