249386
249386
// TnzTools includes
249386
#include <tools tool.h=""></tools>
249386
#include <tools toolutils.h=""></tools>
249386
#include <tools toolhandle.h=""></tools>
249386
#include <tools cursors.h=""></tools>
249386
#include <tools assistant.h=""></tools>
3bac66
#include <tools inputmanager.h=""></tools>
249386
249386
// TnzLib includes
249386
#include <toonz tapplication.h=""></toonz>
249386
#include <toonz txshlevelhandle.h=""></toonz>
249386
249386
// TnzCore includes
249386
#include <tgl.h></tgl.h>
249386
#include <tproperty.h></tproperty.h>
249386
#include <tmetaimage.h></tmetaimage.h>
249386
249386
// For Qt translation support
249386
#include <qcoreapplication></qcoreapplication>
249386
249386
#include <map></map>
249386
249386
249386
//-------------------------------------------------------------------
249386
249386
//=============================================================================
249386
// Edit Assistants Undo
249386
//-----------------------------------------------------------------------------
249386
249386
class EditAssistantsUndo final : public ToolUtils::TToolUndo {
249386
private:
d94945
  bool m_isCreated;
d94945
  bool m_isRemoved;
249386
  TMetaObjectP m_metaObject;
249386
  TVariant m_oldData;
249386
  TVariant m_newData;
249386
  size_t m_size;
249386
249386
public:
249386
  EditAssistantsUndo(
249386
    TXshSimpleLevel *level,
249386
    const TFrameId &frameId,
d94945
    bool isCreated,
d94945
    bool isRemoved,
3bac66
    TMetaObjectP metaObject,
249386
    TVariant oldData
249386
  ):
249386
    ToolUtils::TToolUndo(level, frameId),
d94945
    m_isCreated(isCreated),
d94945
    m_isRemoved(isRemoved),
3bac66
    m_metaObject(metaObject),
249386
    m_oldData(oldData),
249386
    m_newData(m_metaObject->data()),
249386
    m_size(m_oldData.getMemSize() + m_newData.getMemSize())
249386
  { }
249386
249386
  int getSize() const override
249386
    { return m_size; }
249386
  QString getToolName() override
249386
    { return QString("Edit Assistants Tool"); }
249386
d94945
  void process(bool remove, const TVariant &data) const {
d94945
    if (TMetaImage *metaImage = dynamic_cast<tmetaimage*>(m_level->getFrame(m_frameId, true).getPointer()))</tmetaimage*>
d94945
    {
d94945
      { // wrap writer
d94945
        TMetaImage::Writer writer(*metaImage);
d94945
        bool found = false;
3bac66
        for(TMetaObjectList::iterator i = writer->begin(); i != writer->end(); ++i)
3bac66
          if ((*i) == m_metaObject) {
d94945
            if (remove) writer->erase(i);
d94945
            found = true;
d94945
            break;
d94945
          }
d94945
        if (!remove) {
d94945
          if (!found)
3bac66
            writer->push_back(m_metaObject);
d94945
          m_metaObject->data() = data;
3bac66
          if (m_metaObject->handler())
3bac66
            m_metaObject->handler()->fixData();
d94945
        }
249386
      }
249386
      notifyImageChanged();
249386
    }
249386
  }
249386
d94945
  void undo() const override
d94945
    { process(m_isCreated, m_oldData); }
d94945
d94945
  void redo() const override
d94945
    { process(m_isRemoved, m_newData); }
249386
};
249386
249386
249386
//=============================================================================
249386
// Edit Assistants Tool
249386
//-----------------------------------------------------------------------------
249386
249386
class EditAssistantsTool final : public TTool {
249386
  Q_DECLARE_TR_FUNCTIONS(EditAssistantsTool)
4da757
protected:
3bac66
  enum Mode {
3bac66
    ModeImage,
3bac66
    ModeAssistant,
3bac66
    ModePoint
3bac66
  };
249386
3bac66
  TPropertyGroup m_allProperties;
3bac66
  TPropertyGroup m_toolProperties;
249386
  TEnumProperty m_assistantType;
249386
  TStringId m_newAssisnantType;
249386
249386
  bool           m_dragging;
3bac66
  TMetaObjectH   m_currentAssistant;
249386
  bool           m_currentAssistantCreated;
3bac66
  bool           m_currentAssistantChanged;
249386
  int            m_currentAssistantIndex;
3bac66
  TVariant       m_currentAssistantBackup;
249386
  int            m_currentPointIndex;
249386
  TPointD        m_currentPointOffset;
249386
  TPointD        m_currentPosition;
249386
  TGuidelineList m_currentGuidelines;
249386
3bac66
  TMetaImage::Reader *m_reader;
3bac66
  TMetaImage         *m_readImage;
3bac66
  TMetaObjectPC       m_readObject;
3bac66
  const TAssistant   *m_readAssistant;
3bac66
3bac66
  TMetaImage::Writer *m_writer;
3bac66
  TMetaImage         *m_writeImage;
3bac66
  TMetaObjectP        m_writeObject;
3bac66
  TAssistant         *m_writeAssistant;
3bac66
249386
public:
249386
  EditAssistantsTool():
249386
    TTool("T_EditAssistants"),
249386
    m_assistantType("AssistantType"),
249386
    m_dragging(),
249386
    m_currentAssistantCreated(),
3bac66
    m_currentAssistantChanged(),
249386
    m_currentAssistantIndex(-1),
3bac66
    m_currentPointIndex(-1),
3bac66
    m_reader(),
3bac66
    m_readImage(),
3bac66
    m_readAssistant(),
3bac66
    m_writer(),
3bac66
    m_writeImage(),
3bac66
    m_writeAssistant()
249386
  {
249386
    bind(MetaImage);
3bac66
    m_toolProperties.bind(m_assistantType);
249386
    updateTranslation();
249386
  }
3bac66
3bac66
  ~EditAssistantsTool()
3bac66
    { close(); }
3bac66
249386
  ToolType getToolType() const override
249386
    { return TTool::LevelWriteTool; }
249386
  int getCursorId() const override
249386
    { return ToolCursor::StrokeSelectCursor; }
249386
  void onImageChanged() override
249386
    { getViewer()->GLInvalidateAll(); }
249386
4da757
  void updateAssistantTypes() {
4da757
    std::wstring value = m_assistantType.getValue();
4da757
4da757
    m_assistantType.deleteAllValues();
4da757
    m_assistantType.addValueWithUIName(L"", tr("--"));
4da757
4da757
    const TMetaObject::Registry ®istry = TMetaObject::getRegistry();
4da757
    for(TMetaObject::Registry::const_iterator i = registry.begin(); i != registry.end(); ++i)
4da757
      if (const TAssistantType *assistantType = dynamic_cast<const tassistanttype*="">(i->second))</const>
4da757
        if (assistantType->name)
4da757
          m_assistantType.addValueWithUIName(
4da757
            to_wstring(assistantType->name.str()),
4da757
            assistantType->getLocalName() );
4da757
4da757
    if (m_assistantType.indexOf(value) >= 0)
4da757
      m_assistantType.setValue(value);
4da757
  }
4da757
3bac66
  TPropertyGroup* getProperties(int) override {
3bac66
    m_allProperties.clear();
3bac66
    for(int i = 0; i < m_toolProperties.getPropertyCount(); ++i)
3bac66
      m_allProperties.bind( *m_toolProperties.getProperty(i) );
3bac66
    if (Closer closer = read(ModeAssistant)) {
4da757
      m_readAssistant->updateTranslation();
3bac66
      TPropertyGroup &assistantProperties = m_readAssistant->getProperties();
3bac66
      for(int i = 0; i < assistantProperties.getPropertyCount(); ++i)
3bac66
        m_allProperties.bind( *assistantProperties.getProperty(i) );
3bac66
    }
3bac66
    return &m_allProperties;
3bac66
  }
3bac66
4da757
  void onActivate() override
4da757
    { updateAssistantTypes(); }
249386
249386
  void updateTranslation() override {
4da757
    m_assistantType.setQStringName( tr("Assistant Type") );
4da757
    updateAssistantTypes();
4da757
    if (Closer closer = read(ModeAssistant))
4da757
      m_readAssistant->updateTranslation();
249386
  }
249386
3bac66
  bool onPropertyChanged(std::string name/*, bool addToUndo*/) override {
3bac66
    if (TProperty *property = m_toolProperties.getProperty(name)) {
3bac66
      if (name == m_assistantType.getName())
3bac66
        m_newAssisnantType = TStringId::find( to_string(m_assistantType.getValue()) );
3bac66
    } else {
3bac66
      if (Closer closer = write(ModeAssistant, true))
3bac66
        m_writeAssistant->propertyChanged(TStringId::find(name));
3bac66
      //if (addToUndo) apply();
3bac66
      getViewer()->GLInvalidateAll();
3bac66
    }
249386
    return true;
249386
  }
249386
3bac66
protected:
3bac66
  void close() {
3bac66
    m_readAssistant = 0;
3bac66
    m_readObject.reset();
3bac66
    m_readImage = 0;
3bac66
    if (m_reader) delete(m_reader);
3bac66
    m_reader = 0;
3bac66
3bac66
    m_writeAssistant = 0;
3bac66
    m_writeObject.reset();
3bac66
    m_writeImage = 0;
3bac66
    if (m_writer) delete(m_writer);
3bac66
    m_writer = 0;
3bac66
  }
3bac66
3bac66
  bool openRead(Mode mode) {
3bac66
    close();
3bac66
3bac66
    if ( (mode >= ModeAssistant && !m_currentAssistant)
3bac66
      || (mode >= ModeAssistant && m_currentAssistantIndex < 0)
3bac66
      || (mode >= ModePoint && m_currentPointIndex < 0) ) return false;
3bac66
3bac66
    m_readImage = dynamic_cast<tmetaimage*>(getImage(true));</tmetaimage*>
3bac66
    if (m_readImage) {
3bac66
      m_reader = new TMetaImage::Reader(*m_readImage);
3bac66
      if (mode == ModeImage) return true;
3bac66
3bac66
      if ( m_currentAssistantIndex < (int)(*m_reader)->size()
3bac66
        && (**m_reader)[m_currentAssistantIndex] == m_currentAssistant )
3bac66
      {
3bac66
        m_readObject = (**m_reader)[m_currentAssistantIndex];
3bac66
        m_readAssistant = m_readObject->getHandler<tassistant>();</tassistant>
3bac66
        if (mode == ModeAssistant) return true;
3bac66
3bac66
        if (m_currentPointIndex < m_readAssistant->pointsCount()) {
3bac66
          if (mode == ModePoint) return true;
3bac66
        }
3bac66
      }
3bac66
    }
3bac66
3bac66
    close();
3bac66
    return false;
3bac66
  }
3bac66
3bac66
  void touch() {
3bac66
    if (m_writeAssistant && !m_currentAssistantChanged) {
3bac66
      m_currentAssistantBackup = m_writeAssistant->data();
3bac66
      m_currentAssistantChanged = true;
3bac66
    }
3bac66
  }
3bac66
3bac66
  bool openWrite(Mode mode, bool touch = false) {
3bac66
    close();
3bac66
3bac66
    if ( (mode >= ModeAssistant && !m_currentAssistant)
3bac66
      || (mode >= ModeAssistant && m_currentAssistantIndex < 0)
3bac66
      || (mode >= ModePoint && m_currentPointIndex < 0) ) return false;
3bac66
3bac66
    m_writeImage = dynamic_cast<tmetaimage*>(getImage(true));</tmetaimage*>
3bac66
    if (m_writeImage) {
3bac66
      m_writer = new TMetaImage::Writer(*m_writeImage);
3bac66
      if (mode == ModeImage) return true;
3bac66
3bac66
      if ( m_currentAssistantIndex < (int)(*m_writer)->size()
3bac66
        && (**m_writer)[m_currentAssistantIndex] == m_currentAssistant )
3bac66
      {
3bac66
        m_writeObject = (**m_writer)[m_currentAssistantIndex];
3bac66
        m_writeAssistant = m_writeObject->getHandler<tassistant>();</tassistant>
3bac66
        if ( (mode == ModeAssistant)
3bac66
          || (mode == ModePoint && m_currentPointIndex < m_writeAssistant->pointsCount()) )
3bac66
        {
3bac66
          if (touch) this->touch();
3bac66
          return true;
3bac66
        }
3bac66
      }
3bac66
    }
3bac66
3bac66
    close();
3bac66
    return false;
3bac66
  }
3bac66
3bac66
3bac66
  //! helper functions for construction like this:
3bac66
  //!   if (Closer closer = read(ModeAssistant)) { do-something... }
3bac66
  struct Closer {
3bac66
    struct Args {
3bac66
      EditAssistantsTool *owner;
3bac66
      Args(EditAssistantsTool &owner): owner(&owner) { }
3bac66
      operator bool() const //!< declare bool-convertor here to prevent convertion path: Args->Closer->bool
3bac66
        { return owner && (owner->m_reader || owner->m_writer); }
3bac66
      void close()
3bac66
        { if (owner) owner->close(); }
3bac66
    };
3bac66
    Closer(const Args &args): args(args) { }
3bac66
    ~Closer() { args.close(); }
3bac66
    operator bool() const { return args; }
3bac66
  private:
3bac66
    Args args;
3bac66
  };
3bac66
3bac66
  Closer::Args read(Mode mode)
3bac66
    { openRead(mode); return Closer::Args(*this); }
3bac66
  Closer::Args write(Mode mode, bool touch = false)
3bac66
    { openWrite(mode, touch); return Closer::Args(*this); }
3bac66
3bac66
  void updateOptionsBox()
cbbe07
    { getApplication()->getCurrentTool()->notifyToolOptionsBoxChanged(); }
3bac66
3bac66
  void resetCurrentPoint(bool updateOptionsBox = true) {
3bac66
    close();
3bac66
    m_currentAssistant.reset();
249386
    m_currentAssistantCreated = false;
3bac66
    m_currentAssistantChanged = false;
249386
    m_currentAssistantIndex = -1;
249386
    m_currentPointIndex = -1;
249386
    m_currentPointOffset = TPointD();
249386
    m_currentAssistantBackup.reset();
3bac66
    if (updateOptionsBox) this->updateOptionsBox();
249386
  }
249386
3bac66
  bool findCurrentPoint(const TPointD &position, double pixelSize = 1, bool updateOptionsBox = true) {
3bac66
    resetCurrentPoint(false);
3bac66
    if (Closer closer = read(ModeImage)) {
3bac66
      for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i) {
3bac66
        if (!*i) continue;
3bac66
3bac66
        const TAssistant *assistant = (*i)->getHandler<tassistant>();</tassistant>
3bac66
        if (!assistant) continue;
3bac66
3bac66
        assistant->deselectAll();
3bac66
        for(int j = 0; j < assistant->pointsCount() && m_currentAssistantIndex < 0; ++j) {
3bac66
          const TAssistantPoint &p = assistant->points()[j];
3bac66
          TPointD offset = p.position - position;
4da757
          if (norm2(offset) <= p.radius*p.radius*pixelSize*pixelSize) {
3bac66
            m_currentAssistant.set(*i);
3bac66
            m_currentAssistantIndex = i - (*m_reader)->begin();
3bac66
            m_currentPointIndex = j;
3bac66
            m_currentPointOffset = offset;
3bac66
            assistant->selectAll();
3bac66
          }
249386
        }
249386
      }
249386
    }
3bac66
3bac66
    if (updateOptionsBox) this->updateOptionsBox();
3bac66
    return m_currentAssistantIndex >= 0;
249386
  }
249386
3bac66
  bool apply() {
3bac66
    bool success = false;
3bac66
    if (m_currentAssistantChanged || m_currentAssistantCreated) {
3bac66
      if (Closer closer = write(ModePoint)) {
3bac66
        m_writeAssistant->fixData();
3bac66
        TUndoManager::manager()->add(new EditAssistantsUndo(
3bac66
          getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
3bac66
          getCurrentFid(),
3bac66
          m_currentAssistantCreated,
3bac66
          false,
3bac66
          m_writeObject,
3bac66
          m_currentAssistantBackup ));
3bac66
        m_currentAssistantCreated = false;
3bac66
        m_currentAssistantChanged = false;
3bac66
        success = true;
3bac66
      }
3bac66
    }
3bac66
3bac66
    if (success) {
3bac66
      notifyImageChanged();
3bac66
      getApplication()->getCurrentTool()->notifyToolChanged();
3bac66
      getViewer()->GLInvalidateAll();
3bac66
    }
3bac66
3bac66
    return success;
249386
  }
249386
249386
  void leftButtonDown(const TPointD &position, const TMouseEvent&) override {
3bac66
    apply();
249386
    m_dragging = true;
249386
    if (m_newAssisnantType) {
249386
      // create assistant
249386
      resetCurrentPoint();
3bac66
      if (Closer closer = write(ModeImage)) {
3bac66
        TMetaObjectP object(new TMetaObject(m_newAssisnantType));
3bac66
        if (TAssistant *assistant = object->getHandler<tassistant>()) {</tassistant>
249386
          if (assistant->pointsCount()) {
3bac66
            assistant->setDefaults();
249386
            assistant->movePoint(0, position);
249386
            m_currentAssistantCreated = true;
3bac66
            m_currentAssistantChanged = true;
3bac66
            m_currentAssistantIndex = (int)(*m_writer)->size();
3bac66
            m_currentAssistant = object;
249386
            m_currentPointIndex = 0;
249386
            m_currentPointOffset = TPointD();
3bac66
            m_currentAssistantBackup = assistant->data();
249386
          }
3bac66
          (*m_writer)->push_back(object);
249386
        }
249386
      }
249386
      m_newAssisnantType.reset();
3bac66
    } else {
3bac66
      findCurrentPoint(position);
249386
    }
3bac66
249386
    m_currentPosition = position;
249386
    getViewer()->GLInvalidateAll();
249386
  }
249386
249386
  void leftButtonDrag(const TPointD &position, const TMouseEvent&) override {
3bac66
    if (Closer closer = write(ModePoint, true))
3bac66
      m_writeAssistant->movePoint(
3bac66
        m_currentPointIndex,
3bac66
        position + m_currentPointOffset);
249386
    m_currentPosition = position;
249386
    getViewer()->GLInvalidateAll();
249386
  }
249386
249386
  void leftButtonUp(const TPointD &position, const TMouseEvent&) override {
3bac66
    if (Closer closer = write(ModePoint, true))
3bac66
      m_writeAssistant->movePoint(
3bac66
        m_currentPointIndex,
3bac66
        position + m_currentPointOffset);
3bac66
3bac66
    apply();
d94945
    m_assistantType.setIndex(0);
249386
    getApplication()->getCurrentTool()->notifyToolChanged();
3bac66
    emit getApplication()->getCurrentTool()->toolChanged();
249386
    m_currentPosition = position;
249386
    getViewer()->GLInvalidateAll();
249386
    m_dragging = false;
249386
  }
249386
3bac66
  bool keyDown(QKeyEvent *keyEvent) override {
9f1018
    if (keyEvent->key() == Qt::Key_Backspace) {
3bac66
      if (!m_dragging) {
3bac66
        apply();
3bac66
        bool success = false;
3bac66
        if (Closer closer = write(ModeAssistant, true)) {
3bac66
          (*m_writer)->erase((*m_writer)->begin() + m_currentAssistantIndex);
3bac66
          TUndoManager::manager()->add(new EditAssistantsUndo(
3bac66
            getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
3bac66
            getCurrentFid(),
3bac66
            false,
3bac66
            true,
3bac66
            m_writeObject,
3bac66
            m_writeObject->data() ));
3bac66
          success = true;
d94945
        }
3bac66
3bac66
        if (success) {
3bac66
          notifyImageChanged();
3bac66
        }
3bac66
d94945
        resetCurrentPoint();
d94945
        getApplication()->getCurrentTool()->notifyToolChanged();
d94945
        getViewer()->GLInvalidateAll();
d94945
      }
d94945
      return true;
d94945
    }
d94945
    return false;
d94945
  }
d94945
249386
  void draw() override {
249386
    m_currentGuidelines.clear();
249386
249386
    // draw assistants
3bac66
    if (Closer closer = read(ModeImage))
3bac66
    for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i)
249386
      if (*i)
249386
      if (const TAssistant *assistant = (*i)->getHandler<tassistant>())</tassistant>
249386
      {
249386
        assistant->drawEdit(getViewer());
249386
        assistant->getGuidelines(
249386
          m_currentPosition + m_currentPointOffset,
249386
          TAffine(),
249386
          m_currentGuidelines );
249386
      }
249386
249386
    // draw guidelines
249386
    for(TGuidelineList::const_iterator i = m_currentGuidelines.begin(); i != m_currentGuidelines.end(); ++i)
249386
      (*i)->draw();
249386
  }
249386
};
249386
249386
//-------------------------------------------------------------------
249386
249386
EditAssistantsTool editAssistantsTool;