Blob Blame Raw


#include "tools/tool.h"
#include "tools/toolutils.h"
#include "tools/cursors.h"

#include "toonz/tframehandle.h"
#include "toonz/txshlevelhandle.h"
#include "toonz/tscenehandle.h"
#include "toonzqt/selectioncommandids.h"

#include "toonzqt/selection.h"
#include "tproperty.h"
#include "tdata.h"
#include "tconvert.h"
#include "tgl.h"
#include "tstroke.h"
#include "tvectorimage.h"

#include "toonz/hook.h"
#include "toonz/txshlevel.h"
#include "toonz/toonzscene.h"
#include "toonz/txsheet.h"
#include "toonz/txshcell.h"
#include "toonz/txshcolumn.h"
#include "toonz/tstageobjecttree.h"
#include "toonz/txshsimplelevel.h"
#include "toonz/levelproperties.h"
#include "toonz/txsheethandle.h"
#include "tw/keycodes.h"

#include <QMessageBox>
#include <math.h>

// For Qt translation support
#include <QCoreApplication>

using namespace ToolUtils;

class TrackerTool;

//=============================================================================
namespace
{
//-----------------------------------------------------------------------------
//=============================================================================
// TrackerRegionSelection
//-----------------------------------------------------------------------------
// Ancora da definire.
// under construction
class TrackerRegionSelection : public TSelection
{

	TXshLevelP m_level;
	std::set<std::pair<int, int>> m_objtp; // objtp: 1=ObjectId 2=Tracker Region Index
	TrackerTool *m_tool;

public:
	TrackerRegionSelection() : m_tool(0)
	{
	}

	void setTool(TrackerTool *tool) { m_tool = tool; }

	TSelection *clone() const { return new TrackerRegionSelection(*this); }
	void setLevel(const TXshLevelP &level) { m_level = level; }

	void select(int objectId, int trackerRegionIndex)
	{
		m_objtp.insert(std::make_pair(objectId, trackerRegionIndex));
	}
	void deselect(int objectId, int trackerRegionIndex)
	{
		m_objtp.erase(std::make_pair(objectId, trackerRegionIndex));
	}

	bool isSelected(int objectId, int trackerRegionIndex) const
	{
		return m_objtp.count(std::make_pair(objectId, trackerRegionIndex)) > 0;
	}
	void invertSelection(int objectId, int trackerRegionIndex)
	{
		if (isSelected(objectId, trackerRegionIndex))
			deselect(objectId, trackerRegionIndex);
		else
			select(objectId, trackerRegionIndex);
	}

	bool isEmpty() const
	{
		return m_objtp.empty();
	}

	void selectNone() { m_objtp.clear(); }

	HookSet *getHookSet() const
	{
		TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
		//TXshLevel *xl = m_level.getPointer();
		if (!xl)
			return 0;
		return xl->getHookSet();
	}

	TDataP getData() { return TDataP(); }
	TDataP cutData() { return TDataP(); }

	TDataP clearData()
	{
		/*
    //HookData *data = new HookData();
    HookSet *hookSet = getHookSet();
		TFrameId fid = TTool::getApplication()->getCurrentFrame()->getFid();
    if(!hookSet) return TDataP();
    
    for(int i=0;i<hookSet->getHookCount();i++)
      {
       Hook *hook = hookSet->getHook(i);
       if(!hook || hook->isEmpty()) continue;
       if(isSelected(i,1) && isSelected(i,2))
         hookSet->clearHook(hook);
       else if(isSelected(i,2))
         hook->setBPos(fid, hook->getAPos(fid));
       else if(isSelected(i,1))
         hook->setAPos(fid, hook->getBPos(fid));
      }
		makeCurrent();
    return TDataP(); */
	}

	TDataP pasteData(const TDataP &data) { return TDataP(); }

	void resetData(const TDataP &data, bool insert) {}

	bool select(const TSelection *s)
	{
		if (const TrackerRegionSelection *hs = dynamic_cast<const TrackerRegionSelection *>(s)) {
			m_level = hs->m_level;
			m_objtp = hs->m_objtp;
			return true;
		} else
			return false;
	}

	void enableCommands();
	void convertToRegion();
};

} // namespace

//=============================================================================
//  TrackerTool class declaration
//-----------------------------------------------------------------------------

class TrackerTool : public TTool
{
	Q_DECLARE_TR_FUNCTIONS(TrackerTool)

	TrackerRegionSelection m_selection;
	TPointD m_firstPos, m_lastPos;

	int m_hookSelectedIndex;
	int m_lastHookSelectedIndex;
	bool m_deselectArmed;
	bool m_newObjectAdded; // serve al buttonUp per sapere se l'ultima tracker region
						   // aggiunta è un oggetto nuovo oppure no

	TPropertyGroup m_prop;
	TDoubleProperty m_toolSizeWidth;
	TDoubleProperty m_toolSizeHeight;
	TIntProperty m_toolPosX;
	TIntProperty m_toolPosY;
	TRectD m_shapeBBox;

	bool m_buttonDown;
	bool m_dragged;
	bool m_picked;
	TPointD m_pos; // posizione del mouse
	TPointD m_oldPos;

	int m_what;
	enum { Outside,
		   Inside,
		   P00,
		   P01,
		   P10,
		   P11,
		   P1M,
		   PM1,
		   P0M,
		   PM0,
		   ADD_OBJECT,
		   NormalHook };

public:
	TrackerTool();

	ToolType getToolType() const { return TTool::LevelReadTool; }

	void updateTranslation();

	TrackerObjectsSet *getTrackerObjectsSet() const;
	HookSet *getHookSet() const;
	void draw();

	void deleteSelectedTrackerRegion();

	void leftButtonDown(const TPointD &pos, const TMouseEvent &e);
	void leftButtonDrag(const TPointD &pos, const TMouseEvent &e);
	void leftButtonUp(const TPointD &pos, const TMouseEvent &);
	void mouseMove(const TPointD &pos, const TMouseEvent &e);

	bool keyDown(int key, TUINT32 flags, const TPoint &pos);
	// bool moveCursor(const TPointD &pos){}
	void onEnter();
	void onLeave();
	void onActivate();
	void onDeactivate();

	void onImageChanged()
	{
	}
	void reset();
	TPropertyGroup *getProperties(int targetType) { return &m_prop; }

	void onSelectionChanged() { invalidate(); }
	bool onPropertyChanged(std::string propertyName);
	bool select(const TSelection *) { return false; }

	bool pick(int &hookIndex, const TPointD &pos);

	int getCursorId() const;

} trackerTool;

//=============================================================================
//  TrackerTool implementation
//-----------------------------------------------------------------------------

TrackerTool::TrackerTool()
	: TTool("T_Tracker"), m_hookSelectedIndex(-1), m_lastHookSelectedIndex(-1), m_deselectArmed(false), m_toolSizeWidth("Width:", 0, 1000, 10) //W_ToolOptions
	  ,
	  m_toolSizeHeight("Height:", 0, 1000, 10) //W_ToolOptions
	  ,
	  m_toolPosX("X:", -9000, 9000, 10) //W_ToolOptions
	  ,
	  m_toolPosY("Y:", -9000, 9000, 10) //W_ToolOptions
	  ,
	  m_shapeBBox(), m_buttonDown(false), m_dragged(false), m_oldPos(TPointD(0, 0)), m_newObjectAdded(false)
{
	bind(TTool::CommonLevels);
	m_prop.bind(m_toolSizeWidth);
	m_prop.bind(m_toolSizeHeight);
	m_prop.bind(m_toolPosX);
	m_prop.bind(m_toolPosY);
	m_selection.setTool(this);
}

//-----------------------------------------------------------------------------

void TrackerTool::updateTranslation()
{
	m_toolSizeWidth.setQStringName(tr("Width:"));
	m_toolSizeHeight.setQStringName(tr("Height:"));
	m_toolPosX.setQStringName(tr("X:"));
	m_toolPosY.setQStringName(tr("Y:"));
}

//-----------------------------------------------------------------------------

TrackerObjectsSet *TrackerTool::getTrackerObjectsSet() const
{
	HookSet *hookSet = getHookSet();
	if (!hookSet)
		return 0;
	return hookSet->getTrackerObjectsSet();
}
HookSet *TrackerTool::getHookSet() const
{
	TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
	if (!xl)
		return 0;
	return xl->getHookSet();
}

//-----------------------------------------------------------------------------

void TrackerTool::draw()
{
	HookSet *hookSet = getHookSet();
	if (!hookSet)
		return;
	if (hookSet->getHookCount() <= m_hookSelectedIndex)
		m_hookSelectedIndex = -1;

	TFrameId fid = getCurrentFid();
	int selectedObjectId;
	if (m_hookSelectedIndex >= 0 && hookSet->getHook(m_hookSelectedIndex))
		selectedObjectId = hookSet->getHook(m_hookSelectedIndex)->getTrackerObjectId();
	else
		selectedObjectId = -1;
	int i = 0;
	double pixelSize = getPixelSize();

	std::vector<TRectD> balloons; // this is used to avoid balloons overlapping
	// draw hooks
	for (i = 0; i < hookSet->getHookCount(); i++) {
		Hook *hook = hookSet->getHook(i);
		if (!hook || hook->isEmpty())
			continue;
		assert(hook);
		// Se l'Hook ha una TrackerRegion allora la disegno
		if (hook->getTrackerObjectId() >= 0) {
			TRectD rect;
			rect = hook->getTrackerRegion(fid);
			TPixel32 textColor(127, 127, 127);
			TPixel32 trackerObjectColor(0, 0, 0);

			if (hook->getTrackerObjectId() == selectedObjectId) {
				if (m_hookSelectedIndex == i) {
					TPixel32 frameColor(127, 127, 127);
					drawSquare(0.5 * (rect.getP01() + rect.getP11()), pixelSize * 4, frameColor); // scalaY
					drawSquare(0.5 * (rect.getP11() + rect.getP10()), pixelSize * 4, frameColor); // scalaX
					drawSquare(rect.getP00(), pixelSize * 4, frameColor);						  // scala
					drawSquare(rect.getP10(), pixelSize * 4, frameColor);						  // ridimensiona
					trackerObjectColor = TPixel32(183, 227, 0);
					textColor = TPixel32(155, 213, 219);
				} else {
					textColor = TPixel32(183, 227, 0);
					trackerObjectColor = TPixel32(155, 213, 219);
				}
			} else
				trackerObjectColor = TPixel32(0, 0, 0);

			tglColor(trackerObjectColor);
			tglDrawRect(rect);
			tglColor(textColor);
			glPushMatrix();
			glTranslated(hook->getPos(fid).x, hook->getPos(fid).y, 0);
			glScaled(pixelSize, pixelSize, 1);
			int objectId = hook->getTrackerObjectId();
			char *objectChar = (char *)malloc(2);
			objectChar[0] = (char)(objectId + 65);
			objectChar[1] = '\0';
			std::string text(objectChar);
			tglDrawText(TPointD(-15, 10), text);
			glPopMatrix();
		}

		TPointD p0 = hook->getAPos(fid);
		TPointD p1 = hook->getBPos(fid);
		bool linked = p0 == p1;
		drawHook(p0, linked ? ToolUtils::NormalHook : ToolUtils::PassHookA, m_hookSelectedIndex == i);
		std::string hookName = toString(i + 1);
		TPixel32 balloonColor(200, 220, 205, 200);
		TPoint balloonOffset(20, 20);
		drawBalloon(p0, hookName, balloonColor, balloonOffset, false, &balloons);
		if (!linked) {
			drawHook(p1, PassHookB, m_selection.isSelected(i, 2));
			drawBalloon(p1, hookName, balloonColor, balloonOffset, false, &balloons);
		}
	}
}

//-----------------------------------------------------------------------------

bool TrackerTool::pick(int &hookIndex, const TPointD &pos)
{
	double minDistance = -1;
	m_what = Outside;

	HookSet *hookSet = getHookSet();
	if (!hookSet)
		return false;
	TFrameId fid = getCurrentFid();

	double pixelSize = getPixelSize();
	int i = 0;
	for (i = 0; i < (int)hookSet->getHookCount(); i++) {
		Hook *hook = hookSet->getHook(i);
		if (!hook || hook->isEmpty())
			continue;
		int trackerObjectId = hook->getTrackerObjectId();
		if (trackerObjectId < 0) // se non è una trackeRregion
		{
			TPointD hookPos = hook->getPos(fid);
			TRectD overArea = TRectD(hookPos.x - 20 * pixelSize, hookPos.y - 20 * pixelSize,
									 hookPos.x + 20 * pixelSize, hookPos.y + 20 * pixelSize);
			if (overArea.contains(pos)) {
				hookIndex = i; //setto l'indice dell'hook
				m_what = NormalHook;
				return true;
			}
		} else {
			/*
			TrackerObjectsSet *trackerObjectsSet = getTrackerObjectsSet();
			if(!trackerObjectsSet) return false;
			TrackerObject *trackerObject = trackerObjectsSet->getObjectFromIndex(i);
			int j=0;
			for(j=0;j<(int)trackerObject->getHooksCount();j++)
			{*/
			TPointD centerPos = hook->getPos(fid);
			double width = hook->getTrackerRegionWidth();
			double height = hook->getTrackerRegionHeight();
			double distance = tdistance2(centerPos, pos);
			TPointD localPos = pos - centerPos;
			TRectD rect = hook->getTrackerRegion(fid);
			TRectD overArea = TRectD(rect.getP00().x - 4 * pixelSize, rect.getP00().y - 4 * pixelSize,
									 rect.getP11().x + 4 * pixelSize, rect.getP11().y + 4 * pixelSize);
			// se pos è all'interno del'area sensibile del rettangolo
			if (overArea.contains(pos)) {
				if (distance < minDistance || minDistance == -1) {
					minDistance = distance;
					hookIndex = i;

					m_what = Inside;

					// scale Y Area
					double x = 0.5 * (rect.getP01().x + rect.getP11().x); //ascissa punto medio
					TPointD my = TPointD(x, rect.getP11().y);
					TRectD scaleYArea = TRectD(my.x - 4 * pixelSize, my.y - 4 * pixelSize, my.x + 4 * pixelSize, my.y + 4 * pixelSize);
					if (scaleYArea.contains(pos))
						m_what = PM1;
					// scale X Area
					double y = 0.5 * (rect.getP11().y + rect.getP10().y); //ordinata punto medio
					TPointD mx = TPointD(rect.getP10().x, y);
					TRectD scaleXArea = TRectD(mx.x - 4 * pixelSize, mx.y - 4 * pixelSize, mx.x + 4 * pixelSize, mx.y + 4 * pixelSize);
					if (scaleXArea.contains(pos))
						m_what = P1M;
					// resize area (scale X and Y)
					TRectD resizeArea = TRectD(rect.getP10().x - 4 * pixelSize, rect.getP10().y - 4 * pixelSize,
											   rect.getP10().x + 4 * pixelSize, rect.getP10().y + 4 * pixelSize);
					if (resizeArea.contains(pos))
						m_what = P10;
					// scale area

					TRectD scaleArea = TRectD(rect.getP00().x - 4 * pixelSize, rect.getP00().y - 4 * pixelSize,
											  rect.getP00().x + 4 * pixelSize, rect.getP00().y + 4 * pixelSize);
					if (scaleArea.contains(pos))
						m_what = P00;
				}
			}
			//}
		}
	}
	return (minDistance != -1);
}

//-----------------------------------------------------------------------------

void TrackerTool::leftButtonDown(const TPointD &pos, const TMouseEvent &e)
{
	m_buttonDown = true;
	m_picked = true;
	TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
	if (!xl)
		return;
	m_selection.setLevel(xl);
	m_firstPos = m_lastPos = pos;

	m_deselectArmed = false;

	m_oldPos = pos;
	double pixelSize = getPixelSize();

	HookSet *hookSet = xl->getHookSet();
	if (!hookSet)
		return;
	TrackerObjectsSet *trackerObjectsSet = getTrackerObjectsSet();
	TFrameId fid = getCurrentFid();
	if (!trackerObjectsSet)
		return;
	if (pick(m_hookSelectedIndex, pos)) {
		if (m_what == NormalHook) {
			invalidate();
			return;
		}
		Hook *hook = hookSet->getHook(m_hookSelectedIndex);
		if (!hook)
			return;
		m_selection.selectNone();
		m_selection.select(m_hookSelectedIndex, m_hookSelectedIndex);
		m_toolSizeWidth.setValue(hook->getTrackerRegionWidth());
		m_toolSizeHeight.setValue(hook->getTrackerRegionHeight());
		m_toolPosX.setValue(hook->getPos(fid).x);
		m_toolPosY.setValue(hook->getPos(fid).y);
		m_toolSizeWidth.notifyListeners();
		m_toolSizeHeight.notifyListeners();
		m_toolPosX.notifyListeners();
		m_toolPosY.notifyListeners();
	} else {
		m_selection.selectNone();

		if (xl->getSimpleLevel() && !xl->getSimpleLevel()->isReadOnly()) {
			TFrameId fid = getCurrentFid();
			m_what = P10;

			// Se non è selezionato alcun oggetto allora aggiungo un nuovo oggetto e a questo
			// aggiungo una trackerRegion
			int trackerObjectId;
			if (m_hookSelectedIndex == -1 || hookSet->getHook(m_hookSelectedIndex) == 0) {
				trackerObjectId = trackerObjectsSet->addObject();
				m_newObjectAdded = true;
			} else
				trackerObjectId = hookSet->getHook(m_hookSelectedIndex)->getTrackerObjectId();
			// se l'oggetto selezionato è un semplice Hook (senza region)
			// allora creo un nuovo hook con region
			if (trackerObjectId == -1) {
				trackerObjectId = trackerObjectsSet->addObject();
				m_newObjectAdded = true;
			}

			// aggiungo un Hook all'oggetto selezionato
			Hook *newHook = hookSet->addHook();
			if (newHook) {
				newHook->setTrackerObjectId(trackerObjectId);
				newHook->setAPos(fid, pos);
				newHook->setTrackerRegionHeight(pixelSize * 10);
				newHook->setTrackerRegionWidth(pixelSize * 10);
				// setto l'indice della trackerRegion corrente
				m_hookSelectedIndex = newHook->getId(); // hookSet->getHookCount()-1;
			} else {
				if (hookSet->getHookCount() >= 20)
					QMessageBox::warning(0, "TrackerTool Error", "Hooks number must be at most 20");
				m_hookSelectedIndex = -1;
			}
		}
	}
	m_selection.makeCurrent();
	invalidate();
	return;
}

//-----------------------------------------------------------------------------

void TrackerTool::leftButtonDrag(const TPointD &pp, const TMouseEvent &e)
{
	if (!m_buttonDown)
		return;
	if (m_hookSelectedIndex < 0 && m_what != NormalHook)
		return;
	HookSet *hookSet = getHookSet();
	if (!hookSet)
		return;
	assert(hookSet->getHook(m_hookSelectedIndex) != 0);
	TrackerObjectsSet *trackerObjectsSet = getTrackerObjectsSet();
	TFrameId fid = getCurrentFid();
	if (!trackerObjectsSet)
		return;

	if (m_dragged == false) {
		m_dragged = true;
	}

	TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
	if (xl->getSimpleLevel() && xl->getSimpleLevel()->isReadOnly() && m_what != Inside && m_what != NormalHook)
		return;

	if (m_dragged == true) {
		Hook *hook = new Hook();
		hook = getHookSet()->getHook(m_hookSelectedIndex);
		if (!hook || hook->isEmpty())
			return;
		TPointD deltaPos = pp - m_oldPos;
		m_oldPos = pp;
		double newWidth = hook->getTrackerRegionWidth();
		double newHeight = hook->getTrackerRegionHeight();
		TAffine aff;
		const double epsilon = 1e-2;
		TPointD posCenter = hook->getPos(fid);
		double a = norm2(pp - posCenter);
		double b = norm2(pp - deltaPos - posCenter);
		switch (m_what) {
		case Inside: //Traslazione
			hook->setAPos(fid, hook->getPos(fid) + deltaPos);
			break;
		case NormalHook: //Traslazione Hook
		{
			hook->setAPos(fid, hook->getPos(fid) + deltaPos);
			invalidate();
			return;
		}
		case P00: // Scalatura
		{
			if (a <= epsilon || b <= epsilon)
				return;
			aff = TScale(posCenter, sqrt(a / b));
			TRectD rect = hook->getTrackerRegion(fid);
			TPointD av(hook->getTrackerRegion(fid).getP00());
			TPointD av2(hook->getTrackerRegion(fid).getP11());
			av = aff * av;
			av2 = aff * av2;
			rect = TRectD(av, av2);
			newWidth = rect.getLx();
			newHeight = rect.getLy();
			break;
		}
		case P10: // Scalatura X e Y
		{
			TPointD pos = hook->getPos(fid);
			TPointD diffPos = pp - pos;
			int signumx = 1;
			int signumy = 1;
			if (diffPos.x < 0)
				signumx = -1;
			if (diffPos.y < 0)
				signumy = -1;
			newWidth = fabs(hook->getTrackerRegionWidth() + 2 * signumx * deltaPos.x);
			newHeight = fabs(hook->getTrackerRegionHeight() + 2 * signumy * deltaPos.y);
			//double newWidth = fabs(2*diffPos.x-fabs(2*signumx*deltaPos.x));
			//double newHeight = fabs(2*diffPos.y-fabs(2*signumy*deltaPos.y));
			break;
		}
		case P1M: // Ridimensiono X
		{
			TRectD rect = hook->getTrackerRegion(fid);
			rect = rect.enlarge(deltaPos.x, 0);
			newWidth = rect.getLx();
			break;
		}
		case PM1: // Ridimensiono Y
		{
			TRectD rect = hook->getTrackerRegion(fid);
			rect = rect.enlarge(0, deltaPos.y);
			newHeight = rect.getLy();
			break;
		}
		default:
			break;
		}
		if (newWidth > m_toolSizeWidth.getRange().second ||
			newHeight > m_toolSizeHeight.getRange().second)
			return;
		hook->setTrackerRegionWidth(newWidth);
		hook->setTrackerRegionHeight(newHeight);
		m_toolSizeWidth.setValue(hook->getTrackerRegionWidth());
		m_toolSizeHeight.setValue(hook->getTrackerRegionHeight());
		m_toolPosX.setValue(hook->getPos(fid).x);
		m_toolPosY.setValue(hook->getPos(fid).y);
		m_toolPosX.notifyListeners();
		m_toolPosY.notifyListeners();
		m_toolSizeWidth.notifyListeners();
		m_toolSizeHeight.notifyListeners();
	}
	//TTool::Application *app = TTool::getApplication());
	//app->getCurrentScene()->getScene()->getXsheet()->getPegbarTree()->invalidateAll();
	invalidate();
}

//-----------------------------------------------------------------------------

void TrackerTool::leftButtonUp(const TPointD &pos, const TMouseEvent &)
{
	// note: apparently sometimes (when the user triple-clicks) we receive this event twice
	if (!m_buttonDown)
		return;
	// se clicco su una TrackerRegion già selezionato lo deseleziono (per permettere
	// poi l'aggiunta di un nuovo TrackerObject
	if (m_dragged == false &&
		m_hookSelectedIndex == m_lastHookSelectedIndex) {
		m_hookSelectedIndex = -1;
	}
	if (m_newObjectAdded) {
		m_hookSelectedIndex = -1;
		m_newObjectAdded = false;
		// emit signal in order to update schematic
		TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
	}
	m_lastHookSelectedIndex = m_hookSelectedIndex;

	m_dragged = false;
	m_buttonDown = false;

	TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
	if (!xl || !xl->getSimpleLevel())
		return;
	xl->getSimpleLevel()->getProperties()->setDirtyFlag(true);

	//m_selection.selectNone();
	return;
}

//-----------------------------------------------------------------------------

void TrackerTool::deleteSelectedTrackerRegion()
{
	TTool::Application *app = TTool::getApplication();
	TXshLevel *xl = app->getCurrentLevel()->getLevel();
	HookSet *hookSet = xl->getHookSet();
	if (!xl || !xl->getSimpleLevel() || !hookSet || xl->getSimpleLevel()->isReadOnly())
		return;

	//HookUndo *undo = new HookUndo(xl->getSimpleLevel());
	TFrameId fid = getCurrentFid();

	Hook *hook = hookSet->getHook(m_hookSelectedIndex);
	m_hookSelectedIndex = -1;
	if (!hook || hook->isEmpty())
		return;

	hookSet->clearHook(hook);

	//TUndoManager::manager()->add(undo);
	app->getCurrentScene()->getScene()->getXsheet()->getStageObjectTree()->invalidateAll();
	invalidate();

	// emit signal in order to update schematic
	TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();

	/*
	int a = m_selectedObjectId;
	int b = m_selectedTrackerRegionIndex;
	if(m_selectedObjectId<0 ||m_selectedTrackerRegionIndex<0) return;
	TrackerObjectsSet *trackerObjectsSet = getTrackerObjectsSet();
	TFrameId fid = getCurrentFid();
  if(!trackerObjectsSet) return;
	TrackerObject *trackerObject= trackerObjectsSet->getObject(m_selectedObjectId);
	if(trackerObject==NULL) return;
	if(trackerObject->getHooksCount()==0)
	{
		trackerObjectsSet->removeObject(m_selectedObjectId);
		m_selectedObjectId = -1;
		m_selectedTrackerRegionIndex = -1;
		return;
	}
	trackerObject->removeHook(m_selectedTrackerRegionIndex);
	m_selectedTrackerRegionIndex = trackerObject->getHooksCount()-1;
  invalidate(); */
}

//-----------------------------------------------------------------------------

void TrackerTool::mouseMove(const TPointD &pos, const TMouseEvent &e)
{
	m_picked = true;
	if (m_dragged == false) {
		int hookSelectedIndex;
		pick(hookSelectedIndex, pos);
		if (hookSelectedIndex < 0) {
			m_pos = pos;
			m_picked = false;
		}
		invalidate();
	}
}

//-----------------------------------------------------------------------------
bool TrackerTool::onPropertyChanged(std::string propertyName)
{
	HookSet *hookSet = getHookSet();
	if (!hookSet || m_hookSelectedIndex < 0)
		return false;
	TFrameId fid = getCurrentFid();
	Hook *hook = hookSet->getHook(m_hookSelectedIndex);
	if (!hook || hook->isEmpty())
		return false;
	if (propertyName == "Width:") {
		double width = m_toolSizeWidth.getValue();
		hook->setTrackerRegionWidth(width);
	}
	if (propertyName == "Height:") {
		double height = m_toolSizeHeight.getValue();
		hook->setTrackerRegionHeight(height);
	}
	if (propertyName == "X:") {
		double x = m_toolPosX.getValue();
		TPointD pos = hook->getPos(fid);
		pos.x = x;
		hook->setAPos(fid, pos);
	}
	if (propertyName == "Y:") {
		double y = m_toolPosY.getValue();
		TPointD pos = hook->getPos(fid);
		pos.y = y;
		hook->setAPos(fid, pos);
	}
	invalidate();
	return true;
}
void TrackerTool::reset()
{
	m_hookSelectedIndex = -1;
}
int TrackerTool::getCursorId() const
{
	switch (m_what) {
	case Outside:
		return ToolCursor::TrackerCursor;
	case Inside:
		return ToolCursor::MoveCursor;
	case NormalHook:
		return ToolCursor::MoveCursor;
	case P00:
		return ToolCursor::ScaleCursor;
	case P01:
		return ToolCursor::MoveCursor;
	case P10:
		return ToolCursor::ScaleCursor;
	case P1M:
		return ToolCursor::ScaleHCursor;
	case PM1:
		return ToolCursor::ScaleVCursor;
	case ADD_OBJECT:
		return ToolCursor::SplineEditorCursorAdd;
	default:
		return ToolCursor::TrackerCursor;
	}
	return ToolCursor::TrackerCursor;
}
bool TrackerTool::keyDown(int key, TUINT32 flags, const TPoint &pos)
{
	TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
	if (!xl)
		return false;
	HookSet *hookSet = xl->getHookSet();
	if (!hookSet)
		return false;
	TFrameId fid = getCurrentFid();
	Hook *hook = hookSet->getHook(m_hookSelectedIndex);
	if (!hook || hook->isEmpty())
		return false;
	TPointD hookPos = hook->getPos(fid);
	TPointD delta(0, 0);
	double pixelSize = getPixelSize();

	if (key == TwConsts::TK_UpArrow)
		delta.y = 1;
	else if (key == TwConsts::TK_DownArrow)
		delta.y = -1;
	else if (key == TwConsts::TK_LeftArrow)
		delta.x = -1;
	else if (key == TwConsts::TK_RightArrow)
		delta.x = 1;
	else if (key == TwConsts::TK_PageUp) //converto in Hook
		hook->setTrackerObjectId(-1);
	else if (key == TwConsts::TK_PageDown) // converto in trackerRegion
	{
		TrackerObjectsSet *trackerObjectsSet = getTrackerObjectsSet();
		if (!trackerObjectsSet)
			return false;
		int trackerObjectId = hook->getTrackerObjectId();
		if (trackerObjectId != -1)
			return false;
		trackerObjectId = trackerObjectsSet->addObject();
		hook->setTrackerObjectId(trackerObjectId);
		hook->setTrackerRegionHeight(pixelSize * 20);
		hook->setTrackerRegionWidth(pixelSize * 20);
	} else
		return false;

	hookPos += delta;
	hook->setAPos(fid, hookPos);
	//TTool::getApplication()->getCurrentTool()->getTool()->notifyImageChanged();
	//  TNotifier::instance()->notify(TLevelChange());

	return true;
}
// bool moveCursor(const TPointD &pos){}
void TrackerTool::onEnter()
{
	HookSet *hookSet = getHookSet();
	if (!hookSet || hookSet->getHookCount() <= m_hookSelectedIndex)
		m_hookSelectedIndex = -1;
}

//-----------------------------------------------------------------------------

void TrackerTool::onLeave()
{

	TrackerObjectsSet *trackerObjectsSet = getTrackerObjectsSet();
	if (trackerObjectsSet)
		trackerObjectsSet->clearAll();
}

//-----------------------------------------------------------------------------

void TrackerTool::onActivate()
{
}

//-----------------------------------------------------------------------------

void TrackerTool::onDeactivate()
{
	//m_selection.selectNone();
	//TSelection::setCurrent(0);
}

//=============================================================================

TTool *getTrackerToolTool() { return &trackerTool; }

//=============================================================================

void TrackerRegionSelection::enableCommands()
{
	enableCommand(m_tool, MI_Clear, &TrackerTool::deleteSelectedTrackerRegion);
	//enableCommand(m_tool, MI_ConvertToRegion, &TrackerRegionSelection::convertToRegion);
}

void TrackerRegionSelection::convertToRegion()
{
	int i = 0;
	TXshLevel *xl = TTool::getApplication()->getCurrentLevel()->getLevel();
	if (!xl)
		return;
	HookSet *hookSet = xl->getHookSet();
	if (!hookSet)
		return;
	for (i = 0; i < hookSet->getHookCount(); i++) {
		if (isSelected(i, i)) {
			int trackerObjectId = hookSet->getTrackerObjectsSet()->addObject();
			Hook *hook = hookSet->getHook(i);
			if (!hook || hook->isEmpty())
				continue;
			hook->setTrackerObjectId(trackerObjectId);
		}
	}
}