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