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>
da847a
#include <tools replicator.h=""></tools>
3bac66
#include <tools inputmanager.h=""></tools>
249386
249386
// TnzLib includes
249386
#include <toonz tapplication.h=""></toonz>
249386
#include <toonz txshlevelhandle.h=""></toonz>
8f7a4e
#include <toonz txsheethandle.h=""></toonz>
6cb657
#include <toonz tscenehandle.h=""></toonz>
6cb657
#include <toonz tcolumnhandle.h=""></toonz>
bae79a
#include <toonz tframehandle.h=""></toonz>
bae79a
#include <toonz dpiscale.h=""></toonz>
6cb657
#include <toonz toonzscene.h=""></toonz>
6cb657
#include <toonz sceneproperties.h=""></toonz>
249386
249386
// TnzCore includes
249386
#include <tgl.h></tgl.h>
249386
#include <tproperty.h></tproperty.h>
249386
#include <tmetaimage.h></tmetaimage.h>
6cb657
#include <tpixelutils.h></tpixelutils.h>
249386
4dfb4a
#include <toonzqt selection.h=""></toonzqt>
4dfb4a
#include <toonzqt selectioncommandids.h=""></toonzqt>
1715cd
#include <toonzqt tselectionhandle.h=""></toonzqt>
1715cd
249386
// For Qt translation support
249386
#include <qcoreapplication></qcoreapplication>
249386
249386
#include <map></map>
249386
249386
249386
//-------------------------------------------------------------------
249386
249386
//=============================================================================
da847a
// Edit Assistants Swap Undo
da847a
//-----------------------------------------------------------------------------
da847a
da847a
class EditAssistantsReorderUndo final : public ToolUtils::TToolUndo {
da847a
private:
da847a
  int m_oldPos;
da847a
  int m_newPos;
da847a
da847a
public:
da847a
  EditAssistantsReorderUndo(
da847a
    TXshSimpleLevel *level,
da847a
    const TFrameId &frameId,
da847a
    int m_oldPos,
da847a
    int m_newPos
da847a
  ):
da847a
    ToolUtils::TToolUndo(level, frameId),
da847a
    m_oldPos(m_oldPos),
da847a
    m_newPos(m_newPos)
da847a
  { }
da847a
da847a
  QString getToolName() override
da847a
    { return QString("Edit Assistants Tool"); }
da847a
da847a
  int getSize() const override
da847a
    { return sizeof(*this); }
da847a
da847a
  void process(int oldPos, int newPos) const {
da847a
    if (oldPos == newPos || oldPos < 0 || newPos < 0)
da847a
      return;
da847a
    TMetaImage *metaImage = dynamic_cast<tmetaimage*>(m_level->getFrame(m_frameId, true).getPointer());</tmetaimage*>
da847a
    if (!metaImage)
da847a
      return;
da847a
    
da847a
    TMetaImage::Writer writer(*metaImage);
da847a
    TMetaObjectList &list = *writer;
da847a
    if (oldPos >= list.size() || newPos >= writer->size())
da847a
      return;
da847a
    
da847a
    TMetaObjectP obj = list[oldPos];
da847a
    list.erase(list.begin() + oldPos);
da847a
    list.insert(list.begin() + newPos, obj);
da847a
  }
da847a
da847a
  void undo() const override {
da847a
    process(m_newPos, m_oldPos);
da847a
    TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
da847a
    notifyImageChanged();
da847a
  }
da847a
da847a
  void redo() const override {
da847a
    process(m_oldPos, m_newPos);
da847a
    TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
da847a
    notifyImageChanged();
da847a
  }
da847a
};
da847a
da847a
da847a
//=============================================================================
249386
// Edit Assistants Undo
249386
//-----------------------------------------------------------------------------
249386
249386
class EditAssistantsUndo final : public ToolUtils::TToolUndo {
249386
private:
d94945
  bool m_isCreated;
d94945
  bool m_isRemoved;
c5e805
  int m_index;
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,
8f7a4e
    bool frameCreated,
8f7a4e
    bool levelCreated,
8f7a4e
    bool objectCreated,
8f7a4e
    bool objectRemoved,
c5e805
    int index,
3bac66
    TMetaObjectP metaObject,
249386
    TVariant oldData
249386
  ):
8f7a4e
    ToolUtils::TToolUndo(level, frameId, frameCreated, levelCreated),
8f7a4e
    m_isCreated(objectCreated),
8f7a4e
    m_isRemoved(objectRemoved),
c5e805
    m_index(index),
3bac66
    m_metaObject(metaObject),
249386
    m_oldData(oldData),
249386
    m_newData(m_metaObject->data()),
da847a
    m_size(sizeof(*this) + 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 {
c5e805
    if (TMetaImage *metaImage = dynamic_cast<tmetaimage*>(m_level->getFrame(m_frameId, true).getPointer())) {</tmetaimage*>
c5e805
      TMetaImage::Writer writer(*metaImage);
c5e805
      bool found = false;
c5e805
      for(TMetaObjectList::iterator i = writer->begin(); i != writer->end(); ++i)
c5e805
        if ((*i) == m_metaObject) {
c5e805
          if (remove) writer->erase(i);
c5e805
          found = true;
c5e805
          break;
d94945
        }
c5e805
      if (!remove) {
c5e805
        if (!found)
c5e805
          writer->insert(
c5e805
            writer->begin() + std::max(0, std::min((int)writer->size(), m_index)),
c5e805
            m_metaObject );
c5e805
        m_metaObject->data() = data;
c5e805
        if (m_metaObject->handler())
c5e805
          m_metaObject->handler()->fixData();
249386
      }
249386
    }
249386
  }
249386
8f7a4e
  void undo() const override {
8f7a4e
    removeLevelAndFrameIfNeeded();
8f7a4e
    process(m_isCreated, m_oldData);
8f7a4e
    TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
8f7a4e
    notifyImageChanged();
8f7a4e
  }
d94945
8f7a4e
  void redo() const override {
8f7a4e
    insertLevelAndFrameIfNeeded();
8f7a4e
    process(m_isRemoved, m_newData);
8f7a4e
    TTool::getApplication()->getCurrentXsheet()->notifyXsheetChanged();
8f7a4e
    notifyImageChanged();
8f7a4e
  }
249386
};
249386
249386
249386
//=============================================================================
249386
// Edit Assistants Tool
249386
//-----------------------------------------------------------------------------
249386
249386
class EditAssistantsTool final : public TTool {
249386
  Q_DECLARE_TR_FUNCTIONS(EditAssistantsTool)
4dfb4a
public:
4dfb4a
  class Selection final : public TSelection {
4dfb4a
  private:
4dfb4a
    EditAssistantsTool &tool;
4dfb4a
  public:
4dfb4a
    explicit Selection(EditAssistantsTool &tool):
4dfb4a
      tool(tool) { }
4dfb4a
    void deleteSelection() 
4dfb4a
      { tool.removeSelected(); }
4dfb4a
4dfb4a
    void enableCommands() override
4dfb4a
      { if (!isEmpty()) enableCommand(this, MI_Clear, &Selection::deleteSelection); }
4dfb4a
    bool isEmpty() const override
4dfb4a
      { return !tool.isSelected(); }
4dfb4a
    void selectNone() override
4dfb4a
      { tool.deselect(); }
4dfb4a
  };
4dfb4a
  
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;
da847a
  TIntProperty m_replicatorIndex;
249386
  TStringId m_newAssisnantType;
249386
249386
  bool           m_dragging;
aedcda
  bool           m_dragAllPoints;
9bb114
  TSmartHolderT<timage> m_currentImage;</timage>
3bac66
  TMetaObjectH   m_currentAssistant;
249386
  bool           m_currentAssistantCreated;
3bac66
  bool           m_currentAssistantChanged;
249386
  int            m_currentAssistantIndex;
3bac66
  TVariant       m_currentAssistantBackup;
c5e805
  TStringId      m_currentPointName;
249386
  TPointD        m_currentPointOffset;
249386
  TPointD        m_currentPosition;
249386
  TGuidelineList m_currentGuidelines;
da847a
  
da847a
  TReplicator::PointList m_replicatorPoints;
da847a
  
da847a
  TMetaImage::Reader   *m_reader;
da847a
  TMetaImage           *m_readImage;
da847a
  TMetaObjectPC         m_readObject;
da847a
  const TAssistantBase *m_readAssistant;
249386
da847a
  TMetaImage::Writer   *m_writer;
da847a
  TMetaImage           *m_writeImage;
da847a
  TMetaObjectP          m_writeObject;
da847a
  TAssistantBase       *m_writeAssistant;
3bac66
4dfb4a
  Selection *selection;
534c7c
  
534c7c
  // don't try to follow the pointer from history, it may be invalidated
534c7c
  typedef std::pair<const int="" void*,=""> HistoryItem;</const>
534c7c
  typedef std::vector<historyitem> History;</historyitem>
534c7c
  History m_history;
4dfb4a
249386
public:
249386
  EditAssistantsTool():
249386
    TTool("T_EditAssistants"),
249386
    m_assistantType("AssistantType"),
da847a
    m_replicatorIndex("ReplicatorIndex", 1, 10, 1, false),
249386
    m_dragging(),
aedcda
    m_dragAllPoints(),
249386
    m_currentAssistantCreated(),
3bac66
    m_currentAssistantChanged(),
249386
    m_currentAssistantIndex(-1),
3bac66
    m_reader(),
3bac66
    m_readImage(),
3bac66
    m_readAssistant(),
3bac66
    m_writer(),
3bac66
    m_writeImage(),
3bac66
    m_writeAssistant()
249386
  {
4dfb4a
    selection = new Selection(*this);
534c7c
    // also assign assistants to the "brush" button in toolbar
534c7c
    bind(MetaImage | EmptyTarget, "T_Brush");
3bac66
    m_toolProperties.bind(m_assistantType);
da847a
    m_replicatorIndex.setSpinner();
249386
    updateTranslation();
249386
  }
3bac66
4dfb4a
  ~EditAssistantsTool() {
4dfb4a
    close();
4dfb4a
    delete selection;
4dfb4a
  }
3bac66
249386
  ToolType getToolType() const override
249386
    { return TTool::LevelWriteTool; }
fa009d
  unsigned int getToolHints() const override
da847a
    { return TTool::getToolHints() & ~(HintAssistantsAll | HintReplicatorsAll); }
249386
  int getCursorId() const override
249386
    { return ToolCursor::StrokeSelectCursor; }
9bb114
  void onImageChanged() override {
534c7c
    if (m_currentImage != getImage(false)) loadHistory();
9bb114
    getViewer()->GLInvalidateAll();
9bb114
  }
249386
4da757
  void updateAssistantTypes() {
4da757
    std::wstring value = m_assistantType.getValue();
4da757
4da757
    m_assistantType.deleteAllValues();
534c7c
    m_assistantType.addValueWithUIName(L"", tr("<choose create="" to="">"));</choose>
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();
da847a
      if (int i = readReplicatorIndex(*m_reader)) {
da847a
        m_replicatorIndex.setValue(i);
da847a
        m_allProperties.bind( m_replicatorIndex );
da847a
      }
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
9bb114
  void onActivate() override {
9bb114
    updateAssistantTypes();
534c7c
    loadHistory();
9bb114
  }
9bb114
9bb114
  void onDeactivate() override
534c7c
    { resetCurrentPoint(true, false); }
249386
249386
  void updateTranslation() override {
4da757
    m_assistantType.setQStringName( tr("Assistant Type") );
da847a
    m_replicatorIndex.setQStringName( tr("Order") );
4da757
    updateAssistantTypes();
4da757
    if (Closer closer = read(ModeAssistant))
4da757
      m_readAssistant->updateTranslation();
249386
  }
249386
d07d8b
  bool onPropertyChanged(std::string name, bool addToUndo) override {
da847a
    if (m_replicatorIndex.getName() == name) {
da847a
      writeReplicatorIndex(m_replicatorIndex.getValue());
da847a
    } else
da847a
    if (name == m_assistantType.getName()) {
da847a
      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));
d07d8b
      if (addToUndo) apply();
3bac66
      getViewer()->GLInvalidateAll();
3bac66
    }
249386
    return true;
249386
  }
4dfb4a
  
4dfb4a
  TSelection* getSelection() override
4dfb4a
    { return isSelected() ? selection : 0; }
4dfb4a
  
3bac66
protected:
534c7c
  void putHistory(const void *img, int assistantIndex) {
534c7c
    if (!img) return;
534c7c
    for(History::iterator i = m_history.begin(); i != m_history.end(); )
534c7c
      if (i->first == img) i = m_history.erase(i); else ++i;
534c7c
    if (m_history.size() >= 10) m_history.pop_back();
534c7c
    m_history.push_back(HistoryItem(img, assistantIndex));
534c7c
  }
534c7c
  
534c7c
  void loadHistory() {
534c7c
    int index = -1;
534c7c
    if (Closer closer = read(ModeImage))
534c7c
      for(History::iterator i = m_history.begin(); i != m_history.end(); ++i)
534c7c
        if (i->first == m_readImage) index = i->second;
534c7c
    if (index < 0) resetCurrentPoint(true, false); else chooseAssistant(index);
534c7c
  }
534c7c
  
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)
c5e805
      || (mode >= ModePoint && !m_currentPointName) ) return false;
3bac66
4dfb4a
    m_readImage = dynamic_cast<tmetaimage*>(getImage(false));</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];
da847a
        m_readAssistant = m_readObject->getHandler<tassistantbase>();</tassistantbase>
3bac66
        if (mode == ModeAssistant) return true;
3bac66
c5e805
        if (m_readAssistant->findPoint(m_currentPointName)) {
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)
c5e805
      || (mode >= ModePoint && !m_currentPointName) ) 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];
da847a
        m_writeAssistant = m_writeObject->getHandler<tassistantbase>();</tassistantbase>
3bac66
        if ( (mode == ModeAssistant)
c5e805
          || (mode == ModePoint && m_writeAssistant->findPoint(m_currentPointName)) )
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
534c7c
  void resetCurrentPoint(bool updateOptionsBox = true, bool updateHistory = true) {
3bac66
    close();
9bb114
    m_currentImage.reset();
3bac66
    m_currentAssistant.reset();
249386
    m_currentAssistantCreated = false;
3bac66
    m_currentAssistantChanged = false;
249386
    m_currentAssistantIndex = -1;
c5e805
    m_currentPointName.reset();
249386
    m_currentPointOffset = TPointD();
249386
    m_currentAssistantBackup.reset();
9bb114
9bb114
    // deselect points
534c7c
    if (Closer closer = read(ModeImage)) {
9bb114
      for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i)
9bb114
        if (*i)
da847a
          if (const TAssistantBase *assistant = (*i)->getHandler<tassistantbase>())</tassistantbase>
9bb114
            assistant->deselectAll();
534c7c
      if (updateHistory) putHistory(m_readImage, -1);
534c7c
    }
9bb114
3bac66
    if (updateOptionsBox) this->updateOptionsBox();
249386
  }
249386
534c7c
  bool chooseAssistant(int index) {
534c7c
    resetCurrentPoint(false);
534c7c
    if (index >= 0)
534c7c
    if (Closer closer = read(ModeImage)) {
534c7c
      m_currentImage.set(m_readImage);
534c7c
      if (index < (*m_reader)->size())
534c7c
      if (const TMetaObjectPC &obj = (**m_reader)[index])
d9135c
      if (const TAssistantBase *assistant = obj->getHandler<tassistantbase>()) {</tassistantbase>
534c7c
        assistant->deselectAll();
534c7c
        m_currentAssistant.set(obj);
534c7c
        m_currentAssistantIndex = index;
534c7c
        m_currentPointName = assistant->getBasePoint().name;
534c7c
        assistant->selectAll();
534c7c
      }
534c7c
      putHistory(m_readImage, m_currentAssistantIndex);
534c7c
    }
534c7c
    this->updateOptionsBox();
534c7c
    return m_currentAssistantIndex >= 0;
534c7c
  }
534c7c
  
3bac66
  bool findCurrentPoint(const TPointD &position, double pixelSize = 1, bool updateOptionsBox = true) {
3bac66
    resetCurrentPoint(false);
3bac66
    if (Closer closer = read(ModeImage)) {
9bb114
      m_currentImage.set(m_readImage);
3bac66
      for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i) {
3bac66
        if (!*i) continue;
3bac66
da847a
        const TAssistantBase *assistant = (*i)->getHandler<tassistantbase>();</tassistantbase>
3bac66
        if (!assistant) continue;
3bac66
3bac66
        assistant->deselectAll();
7900ea
7900ea
        // last points is less significant and don't affecting the first points
7900ea
        // so iterate points in reverse order to avoid unsolvable points overlapping
7900ea
        const TAssistantPointOrder &points = assistant->pointsOrder();
7900ea
        for( TAssistantPointOrder::const_reverse_iterator j = points.rbegin();
7900ea
             j != points.rend() && m_currentAssistantIndex < 0;
7900ea
             ++j )
7900ea
        {
7900ea
          const TAssistantPoint &p = **j;
3bac66
          TPointD offset = p.position - position;
c5e805
          if (p.visible && norm2(offset) <= p.radius*p.radius*pixelSize*pixelSize) {
3bac66
            m_currentAssistant.set(*i);
3bac66
            m_currentAssistantIndex = i - (*m_reader)->begin();
7900ea
            m_currentPointName = p.name;
3bac66
            m_currentPointOffset = offset;
3bac66
            assistant->selectAll();
3bac66
          }
249386
        }
249386
      }
534c7c
      putHistory(m_readImage, m_currentAssistantIndex);
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) {
7900ea
      if (Closer closer = write(ModeAssistant)) {
3bac66
        m_writeAssistant->fixData();
3bac66
        TUndoManager::manager()->add(new EditAssistantsUndo(
3bac66
          getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
3bac66
          getCurrentFid(),
8f7a4e
          m_isFrameCreated,
8f7a4e
          m_isLevelCreated,
3bac66
          m_currentAssistantCreated,
3bac66
          false,
c5e805
          m_currentAssistantIndex,
3bac66
          m_writeObject,
3bac66
          m_currentAssistantBackup ));
3bac66
        m_currentAssistantCreated = false;
3bac66
        m_currentAssistantChanged = false;
8f7a4e
        m_isFrameCreated = false;
8f7a4e
        m_isLevelCreated = false;
3bac66
        success = true;
3bac66
      }
3bac66
    }
3bac66
3bac66
    if (success) {
3bac66
      notifyImageChanged();
3bac66
      getApplication()->getCurrentTool()->notifyToolChanged();
4dfb4a
      TTool::getApplication()->getCurrentSelection()->setSelection( getSelection() );
3bac66
      getViewer()->GLInvalidateAll();
3bac66
    }
3bac66
3bac66
    return success;
249386
  }
4dfb4a
  
da847a
  int readReplicatorIndex(TMetaImage::Reader &reader) {
da847a
    int cnt = (int)reader->size();
da847a
    int index = 0;
da847a
    for(int i = 0; i < cnt; ++i) {
da847a
      if (const TMetaObjectPC &obj = (*reader)[i])
da847a
      if (obj->getHandler<treplicator>()) {</treplicator>
da847a
        ++index;
da847a
        if (m_currentAssistantIndex == i)
da847a
          return index;
da847a
      }
da847a
    }
da847a
    return 0;
da847a
  }
da847a
da847a
  void writeReplicatorIndex(int index) {
da847a
    apply();
da847a
    
da847a
    int wantIndex = index;
da847a
    int oldPos = -1;
da847a
    int newPos = -1;
da847a
    bool changed = false;
da847a
    
da847a
    if (Closer closer = write(ModeAssistant)) {
da847a
      if (index < 1)
da847a
        index = 1;
da847a
      
da847a
      int idx = 0;
da847a
      int lastPos = -1;
da847a
      
da847a
      TMetaObjectList &list = **m_writer;
da847a
      
da847a
      int cnt = (int)list.size();
da847a
      for(int i = 0; i < cnt; ++i) {
da847a
        if (list[i] && list[i]->getHandler<treplicator>()) {</treplicator>
da847a
          ++idx;
da847a
          if (m_currentAssistantIndex == i) oldPos = i;
da847a
          if (idx == index) newPos = i;
da847a
          lastPos = i;
da847a
        }
da847a
      }
da847a
      
da847a
      if (oldPos >= 0 && lastPos >= 0) {
da847a
        assert(idx);
da847a
        if (newPos < 0)
da847a
          { index = idx; newPos = lastPos; }
da847a
        if (oldPos != newPos) {
da847a
          TMetaObjectP obj = list[oldPos];
da847a
          list.erase(list.begin() + oldPos);
da847a
          list.insert(list.begin() + newPos, obj);
da847a
          
da847a
          TUndoManager::manager()->add(new EditAssistantsReorderUndo(
da847a
            getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
da847a
            getCurrentFid(),
da847a
            oldPos,
da847a
            newPos ));
da847a
          
da847a
          changed = true;
da847a
        }
da847a
      }
da847a
    }
da847a
    
da847a
    if (changed) {
da847a
      m_currentAssistantIndex = newPos;
da847a
      invalidate();
da847a
    }
da847a
    
da847a
    if (wantIndex != index)
da847a
      this->updateOptionsBox();
da847a
  }
da847a
  
8f7a4e
public:
4dfb4a
  void deselect()
534c7c
    { resetCurrentPoint(true, false); }
4dfb4a
  
4dfb4a
  bool isSelected()
4dfb4a
    { return read(ModeAssistant); }
4dfb4a
  
4dfb4a
  bool removeSelected() {
4dfb4a
    apply();
4dfb4a
    bool success = false;
4dfb4a
    if (Closer closer = write(ModeAssistant, true)) {
4dfb4a
      (*m_writer)->erase((*m_writer)->begin() + m_currentAssistantIndex);
4dfb4a
      TUndoManager::manager()->add(new EditAssistantsUndo(
4dfb4a
          getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
4dfb4a
          getCurrentFid(),
4dfb4a
          false,  // frameCreated
4dfb4a
          false,  // levelCreated
4dfb4a
          false,  // objectCreated
4dfb4a
          true,   // objectRemoved
4dfb4a
          m_currentAssistantIndex, m_writeObject, m_writeObject->data()));
4dfb4a
      success = true;
4dfb4a
    }
4dfb4a
4dfb4a
    if (success) notifyImageChanged();
4dfb4a
4dfb4a
    resetCurrentPoint();
4dfb4a
    getApplication()->getCurrentTool()->notifyToolChanged();
4dfb4a
    TTool::getApplication()->getCurrentSelection()->setSelection( getSelection() );
4dfb4a
    getViewer()->GLInvalidateAll();
4dfb4a
    return success;
4dfb4a
  }
4dfb4a
  
8f7a4e
  bool preLeftButtonDown() override {
af0f93
    if (m_assistantType.getIndex() != 0) touchImage();
4dfb4a
    TTool::getApplication()->getCurrentSelection()->setSelection( getSelection() );
8f7a4e
    return true;
8f7a4e
  }
8f7a4e
aedcda
  void leftButtonDown(const TPointD &position, const TMouseEvent &event) override {
3bac66
    apply();
249386
    m_dragging = true;
aedcda
    m_dragAllPoints = false;
249386
    if (m_newAssisnantType) {
249386
      // create assistant
7900ea
      resetCurrentPoint(false);
3bac66
      if (Closer closer = write(ModeImage)) {
3bac66
        TMetaObjectP object(new TMetaObject(m_newAssisnantType));
da847a
        if (TAssistantBase *assistant = object->getHandler<tassistantbase>()) {</tassistantbase>
c5e805
          assistant->setDefaults();
7900ea
          assistant->move(position);
7900ea
          assistant->selectAll();
4dfb4a
          m_currentImage.set(m_writeImage);
aedcda
          m_dragAllPoints = true;
c5e805
          m_currentAssistantCreated = true;
c5e805
          m_currentAssistantChanged = true;
c5e805
          m_currentAssistantIndex = (int)(*m_writer)->size();
c5e805
          m_currentAssistant = object;
c5e805
          m_currentPointName = assistant->getBasePoint().name;
c5e805
          m_currentPointOffset = TPointD();
c5e805
          m_currentAssistantBackup = assistant->data();
3bac66
          (*m_writer)->push_back(object);
249386
        }
249386
      }
7900ea
      updateOptionsBox();
249386
      m_newAssisnantType.reset();
3bac66
    } else {
985f38
      findCurrentPoint(position, getViewer()->getPixelSize());
aedcda
      if (event.isShiftPressed())
aedcda
        if (Closer closer = read(ModePoint)) {
aedcda
          m_currentPointName = m_readAssistant->getBasePoint().name;
aedcda
          m_currentPointOffset = m_readAssistant->getBasePoint().position - position;
aedcda
          m_dragAllPoints = true;
aedcda
        }
249386
    }
3bac66
249386
    m_currentPosition = position;
249386
    getViewer()->GLInvalidateAll();
249386
  }
249386
249386
  void leftButtonDrag(const TPointD &position, const TMouseEvent&) override {
aedcda
    if (m_dragAllPoints) {
b15438
      if (Closer closer = write(ModeAssistant))
b15438
        if (m_writeAssistant->move(position + m_currentPointOffset))
b15438
          touch();
7900ea
    } else {
b15438
      if (Closer closer = write(ModePoint))
b15438
        if (m_writeAssistant->movePoint(m_currentPointName, position + m_currentPointOffset))
b15438
          touch();
7900ea
    }
249386
    m_currentPosition = position;
249386
    getViewer()->GLInvalidateAll();
249386
  }
249386
249386
  void leftButtonUp(const TPointD &position, const TMouseEvent&) override {
4dfb4a
    if (m_dragAllPoints) {
b15438
      if (Closer closer = write(ModeAssistant))
b15438
        if (m_writeAssistant->move(position + m_currentPointOffset))
b15438
          touch();
7900ea
    } else {
b15438
      if (Closer closer = write(ModePoint))
b15438
        if (m_writeAssistant->movePoint(m_currentPointName, position + m_currentPointOffset))
b15438
          touch();
7900ea
    }
3bac66
3bac66
    apply();
d94945
    m_assistantType.setIndex(0);
249386
    getApplication()->getCurrentTool()->notifyToolChanged();
4dfb4a
    TTool::getApplication()->getCurrentSelection()->setSelection( getSelection() );
249386
    m_currentPosition = position;
249386
    getViewer()->GLInvalidateAll();
aedcda
    m_dragAllPoints = false;
249386
    m_dragging = false;
249386
  }
249386
bae79a
  void mouseMove(const TPointD &position, const TMouseEvent&) override {
bae79a
    m_currentPosition = position;
bae79a
    m_currentPointOffset = TPointD();
bae79a
    getViewer()->GLInvalidateAll();
bae79a
  }
bae79a
249386
  void draw() override {
249386
    m_currentGuidelines.clear();
bae79a
    TPointD position = m_currentPosition + m_currentPointOffset;
6cb657
  
6cb657
    // get current column color
6cb657
    TPixelD color = TAssistantBase::colorBase;
6cb657
    if (TApplication *app = getApplication())
6cb657
    if (TXsheetHandle *XsheetHandle = app->getCurrentXsheet())
6cb657
    if (TXsheet *Xsheet = XsheetHandle->getXsheet())
6cb657
    if (TColumnHandle *columnHandle = app->getCurrentColumn())
6cb657
    if (TXshColumn *column = Xsheet->getColumn( columnHandle->getColumnIndex() ))
6cb657
    {
6cb657
      if (int filterId = column->getColorFilterId())
6cb657
      if (TSceneHandle *sceneHandle = app->getCurrentScene())
6cb657
      if (ToonzScene *scene = sceneHandle->getScene())
6cb657
      if (TSceneProperties *props = scene->getProperties())
6cb657
        color = toPixelD(props->getColorFilterColor( filterId ));
6cb657
      color.m *= column->getOpacity()/255.0;
6cb657
    }
6cb657
249386
    // draw assistants
da847a
    int index = 0;
6cb657
    if (Closer closer = read(ModeImage)) {
6cb657
      TPixelD origColor = TAssistantBase::colorBase;
6cb657
      TAssistantBase::colorBase = color;
6cb657
      for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i) {
6cb657
        if (*i)
6cb657
        if (const TAssistantBase *base = (*i)->getHandler<tassistantbase>())</tassistantbase>
6cb657
        {
6cb657
          if (dynamic_cast<const treplicator*="">(base)) {</const>
6cb657
            ++index;
6cb657
            base->drawEdit(getViewer(), index);
6cb657
          } else {
6cb657
            base->drawEdit(getViewer());
6cb657
          }
6cb657
          
6cb657
          if (const TAssistant *assistant = dynamic_cast<const tassistant*="">(base))</const>
6cb657
            assistant->getGuidelines(
6cb657
              position,
6cb657
              TAffine(),
6cb657
              color,
6cb657
              m_currentGuidelines );
da847a
        }
249386
      }
6cb657
      TAssistantBase::colorBase = origColor;
6cb657
    }
bae79a
    
da847a
    TImage *img = getImage(false);
da847a
    
fa009d
    // draw assistans and guidelines from other layers
fa009d
    TAssistant::scanAssistants(
fa009d
      this,          // tool
fa009d
      &position, 1,  // pointer positions
d9135c
      &m_currentGuidelines, // out guidelines
fa009d
      true,          // draw
fa009d
      false,         // enabled only
fa009d
      false,         // mark enabled
fa009d
      true,          // draw guidelines
da847a
      img );         // skip image
da847a
    
da847a
    // draw replicators from other layers
da847a
    TReplicator::scanReplicators(
da847a
      this,          // tool
da847a
      nullptr,       // in/out points
da847a
      nullptr,       // out modifiers
da847a
      true,          // draw
da847a
      false,         // enabled only
da847a
      false,         // mark enabled
da847a
      false,         // draw points
da847a
      img );         // skip image
da847a
    
da847a
    // draw replicator points
da847a
    m_replicatorPoints.clear();
da847a
    m_replicatorPoints.push_back(position);
da847a
    TReplicator::scanReplicators(
da847a
      this,          // tool
da847a
      &m_replicatorPoints, // in/out points
da847a
      nullptr,       // out modifiers
da847a
      false,         // draw
da847a
      false,         // enabled only
da847a
      false,         // mark enabled
da847a
      true,          // draw points
fa009d
      nullptr );     // skip image
249386
  }
249386
};
249386
249386
//-------------------------------------------------------------------
249386
249386
EditAssistantsTool editAssistantsTool;