diff --git a/toonz/sources/include/tools/assistant.h b/toonz/sources/include/tools/assistant.h
index bd1d8f1..e2edc22 100644
--- a/toonz/sources/include/tools/assistant.h
+++ b/toonz/sources/include/tools/assistant.h
@@ -10,6 +10,7 @@
 #include <tsmartpointer.h>
 #include <tgeometry.h>
 #include <tmetaimage.h>
+#include <tproperty.h>
 
 // std includes
 #include <vector>
@@ -32,6 +33,9 @@
 
 //  Forward declarations
 
+class TProperty;
+class TPropertyGroup;
+
 class TToolViewer;
 class TAssistant;
 class TAssistantPoint;
@@ -90,12 +94,16 @@ public:
 
 class DVAPI TAssistant : public TMetaObjectHandler {
 protected:
+  const TStringId m_idEnabled;
   const TStringId m_idPoints;
   const TStringId m_idX;
   const TStringId m_idY;
+  const TStringId m_idMagnetism;
 
   TAssistantPointList m_points;
 
+  mutable TPropertyGroup m_properties;
+
 public:
   TAssistant(TMetaObject &object);
 
@@ -110,16 +118,33 @@ public:
   void movePoint(int index, const TPointD &position);
   void setPointSelection(int index, bool selected) const;
 
+  bool getEnabled() const
+    { return data()[m_idEnabled].getBool(); }
+  void setEnabled(bool x)
+    { if (getEnabled() != x) data()[m_idEnabled].setBool(x); }
+
+  double getMagnetism() const
+    { return data()[m_idMagnetism].getDouble(); }
+  void setMagnetism(double x)
+    { if (getMagnetism() != x) data()[m_idMagnetism].setDouble(x); }
+
   inline void selectPoint(int index) const
     { setPointSelection(index, true); }
   inline void deselectPoint(int index) const
     { setPointSelection(index, false); }
   inline void selectAll() const
-    { for(int i = 0; i < pointsCount(); ++i) setPointSelection(i, false); }
+    { for(int i = 0; i < pointsCount(); ++i) setPointSelection(i, true); }
   inline void deselectAll() const
     { for(int i = 0; i < pointsCount(); ++i) setPointSelection(i, false); }
 
+  TPropertyGroup& getProperties() const
+    { return m_properties; }
+  void propertyChanged(const TStringId &name)
+    { LockEvents lock(*this); onPropertyChanged(name); }
+
 protected:
+  //! usually called when meta-object created
+  void onSetDefaults() override;
   //! called when part of variant data changed
   void onDataChanged(const TVariant &value) override;
   //! load object data from variant
@@ -130,10 +155,18 @@ protected:
   virtual void onMovePoint(int index, const TPointD &position);
   //! save object data to variant
   virtual void onFixData();
+  //! load all properties from variant
+  virtual void updateProperties();
+  //! load single property from variant
+  virtual void updateProperty(const TStringId &name, const TVariant &value);
+  //! put value from property to variant
+  virtual void onPropertyChanged(const TStringId &name);
 
   void drawSegment(const TPointD &p0, const TPointD &p1, double pixelSize) const;
   void drawPoint(const TAssistantPoint &point, double pixelSize) const;
 
+  void addProperty(TProperty *p, const std::string &title);
+
 public:
   virtual void getGuidelines(const TPointD &position, const TAffine &toTool, TGuidelineList &outGuidelines) const;
   virtual void draw(TToolViewer *viewer) const;
diff --git a/toonz/sources/include/tvariant.h b/toonz/sources/include/tvariant.h
index 5c12682..6da8977 100644
--- a/toonz/sources/include/tvariant.h
+++ b/toonz/sources/include/tvariant.h
@@ -60,7 +60,7 @@ public:
     { return !isIndex(); }
   inline int index() const
     { return m_index; }
-  inline int field() const
+  inline TStringId field() const
     { return m_field; }
 
   inline bool operator== (const TVariantPathEntry &other) const
diff --git a/toonz/sources/tnztools/assistant.cpp b/toonz/sources/tnztools/assistant.cpp
index 69e5ac8..e2f299c 100644
--- a/toonz/sources/tnztools/assistant.cpp
+++ b/toonz/sources/tnztools/assistant.cpp
@@ -2,6 +2,7 @@
 #include <tools/assistant.h>
 
 #include <tgl.h>
+#include <tproperty.h>
 
 #include <limits>
 
@@ -122,10 +123,33 @@ TAssistantPoint::TAssistantPoint(
 
 TAssistant::TAssistant(TMetaObject &object):
   TMetaObjectHandler(object),
+  m_idEnabled("enabled"),
   m_idPoints("points"),
   m_idX("x"),
-  m_idY("y")
-{ }
+  m_idY("y"),
+  m_idMagnetism("magnetism")
+{
+  addProperty( new TBoolProperty(m_idEnabled.str(), getEnabled()),
+               "Enabled" );
+  addProperty( new TDoubleProperty(m_idMagnetism.str(), 0.0, 1.0, getMagnetism()),
+               "Magnetism" );
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
+TAssistant::addProperty(TProperty *p, const std::string &title) {
+  p->setQStringName( QString::fromStdString(title) );
+  m_properties.add(p);
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
+TAssistant::onSetDefaults() {
+  setEnabled(true);
+  setMagnetism(1.0);
+}
 
 //---------------------------------------------------------------------------------------------------
 
@@ -171,6 +195,9 @@ TAssistant::onDataChanged(const TVariant &value) {
       pointData[m_idX].getDouble(),
       pointData[m_idY].getDouble() );
     movePoint(entry.index(), position);
+  } else
+  if (data().getChildPathEntry(value, entry) && entry.isField()) {
+    updateProperty(entry.field(), data()[entry.field()]);
   }
 }
 
@@ -186,6 +213,7 @@ TAssistant::onAllDataChanged() {
       pointData[m_idY].getDouble() );
   }
   fixPoints();
+  updateProperties();
 }
 
 //---------------------------------------------------------------------------------------------------
@@ -210,6 +238,64 @@ TAssistant::onFixData() {
     pointData[m_idX].setDouble( m_points[i].position.x );
     pointData[m_idY].setDouble( m_points[i].position.y );
   }
+  setMagnetism( std::max(0.0, std::min(1.0, getMagnetism())) );
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
+TAssistant::updateProperties() {
+  const TVariantMap &map = data().getMap();
+  for(TVariantMap::const_iterator i = map.begin(); i != map.end(); ++i)
+    if (i->first != m_idPoints)
+      updateProperty(i->first, i->second);
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
+TAssistant::updateProperty(const TStringId &name, const TVariant &value) {
+  TProperty *property = m_properties.getProperty(name);
+  if (!property)
+    return;
+
+  if (TBoolProperty *boolProperty = dynamic_cast<TBoolProperty*>(property)) {
+    boolProperty->setValue( value.getBool() );
+  } else
+  if (TDoubleProperty *doubleProperty = dynamic_cast<TDoubleProperty*>(property)) {
+    doubleProperty->setValue( value.getDouble() );
+  } else
+  if (TStringProperty *stringProperty = dynamic_cast<TStringProperty*>(property)) {
+    stringProperty->setValue( to_wstring(value.getString()) );
+  } else
+  if (TEnumProperty *enumProperty = dynamic_cast<TEnumProperty*>(property)) {
+    enumProperty->setValue( to_wstring(value.getString()) );
+  }
+}
+
+//---------------------------------------------------------------------------------------------------
+
+void
+TAssistant::onPropertyChanged(const TStringId &name) {
+  TProperty *property = m_properties.getProperty(name);
+  if (!property)
+    return;
+
+  if (name == m_idPoints)
+    return;
+
+  if (TBoolProperty *boolProperty = dynamic_cast<TBoolProperty*>(property)) {
+    data()[name].setBool( boolProperty->getValue() );
+  } else
+  if (TDoubleProperty *doubleProperty = dynamic_cast<TDoubleProperty*>(property)) {
+    data()[name].setDouble( doubleProperty->getValue() );
+  } else
+  if (TStringProperty *stringProperty = dynamic_cast<TStringProperty*>(property)) {
+    data()[name].setString( to_string(stringProperty->getValue()) );
+  } else
+  if (TEnumProperty *enumProperty = dynamic_cast<TEnumProperty*>(property)) {
+    data()[name].setString( to_string(enumProperty->getValue()) );
+  }
 }
 
 //---------------------------------------------------------------------------------------------------
diff --git a/toonz/sources/tnztools/editassistantstool.cpp b/toonz/sources/tnztools/editassistantstool.cpp
index 0f17d54..baa9f06 100644
--- a/toonz/sources/tnztools/editassistantstool.cpp
+++ b/toonz/sources/tnztools/editassistantstool.cpp
@@ -5,6 +5,7 @@
 #include <tools/toolhandle.h>
 #include <tools/cursors.h>
 #include <tools/assistant.h>
+#include <tools/inputmanager.h>
 
 // TnzLib includes
 #include <toonz/tapplication.h>
@@ -42,13 +43,13 @@ public:
     const TFrameId &frameId,
     bool isCreated,
     bool isRemoved,
-    TMetaObjectR metaObject,
+    TMetaObjectP metaObject,
     TVariant oldData
   ):
     ToolUtils::TToolUndo(level, frameId),
     m_isCreated(isCreated),
     m_isRemoved(isRemoved),
-    m_metaObject(metaObject.getPointer()),
+    m_metaObject(metaObject),
     m_oldData(oldData),
     m_newData(m_metaObject->data()),
     m_size(m_oldData.getMemSize() + m_newData.getMemSize())
@@ -65,18 +66,18 @@ public:
       { // wrap writer
         TMetaImage::Writer writer(*metaImage);
         bool found = false;
-        for(TMetaObjectRefList::iterator i = writer->begin(); i != writer->end(); ++i)
-          if (*i == m_metaObject) {
+        for(TMetaObjectList::iterator i = writer->begin(); i != writer->end(); ++i)
+          if ((*i) == m_metaObject) {
             if (remove) writer->erase(i);
             found = true;
             break;
           }
         if (!remove) {
           if (!found)
-            writer->push_back(TMetaObjectR(m_metaObject.getPointer()));
+            writer->push_back(m_metaObject);
           m_metaObject->data() = data;
-          if (TMetaObjectHandler *handler = m_metaObject->getHandler<TMetaObjectHandler>())
-            handler->fixData();
+          if (m_metaObject->handler())
+            m_metaObject->handler()->fixData();
         }
       }
       notifyImageChanged();
@@ -98,222 +99,373 @@ public:
 class EditAssistantsTool final : public TTool {
   Q_DECLARE_TR_FUNCTIONS(EditAssistantsTool)
 public:
-  typedef std::map<std::wstring, TStringId> TypeMap;
+  typedef std::map<TStringId, std::string> TypeMap;
+
+  enum Mode {
+    ModeImage,
+    ModeAssistant,
+    ModePoint
+  };
 
 protected:
-  TPropertyGroup m_prop;
+  TPropertyGroup m_allProperties;
+  TPropertyGroup m_toolProperties;
   TEnumProperty m_assistantType;
-  TypeMap m_localnameToType;
+  TypeMap m_typeToName;
   TStringId m_newAssisnantType;
 
   bool           m_dragging;
+  TMetaObjectH   m_currentAssistant;
   bool           m_currentAssistantCreated;
+  bool           m_currentAssistantChanged;
   int            m_currentAssistantIndex;
+  TVariant       m_currentAssistantBackup;
   int            m_currentPointIndex;
   TPointD        m_currentPointOffset;
-  TVariant       m_currentAssistantBackup;
   TPointD        m_currentPosition;
   TGuidelineList m_currentGuidelines;
 
+  TMetaImage::Reader *m_reader;
+  TMetaImage         *m_readImage;
+  TMetaObjectPC       m_readObject;
+  const TAssistant   *m_readAssistant;
+
+  TMetaImage::Writer *m_writer;
+  TMetaImage         *m_writeImage;
+  TMetaObjectP        m_writeObject;
+  TAssistant         *m_writeAssistant;
+
 public:
   EditAssistantsTool():
     TTool("T_EditAssistants"),
     m_assistantType("AssistantType"),
     m_dragging(),
     m_currentAssistantCreated(),
+    m_currentAssistantChanged(),
     m_currentAssistantIndex(-1),
-    m_currentPointIndex(-1)
+    m_currentPointIndex(-1),
+    m_reader(),
+    m_readImage(),
+    m_readAssistant(),
+    m_writer(),
+    m_writeImage(),
+    m_writeAssistant()
   {
     bind(MetaImage);
-    m_prop.bind(m_assistantType);
+    addAssistantType("", "--");
+    addAssistantType("assistantVanishingPoint", "Vanishing Point");
+    m_toolProperties.bind(m_assistantType);
     updateTranslation();
   }
-  
+
+  ~EditAssistantsTool()
+    { close(); }
+
   ToolType getToolType() const override
     { return TTool::LevelWriteTool; }
   int getCursorId() const override
     { return ToolCursor::StrokeSelectCursor; }
-  TPropertyGroup* getProperties(int targetType) override
-    { return &m_prop; }
   void onImageChanged() override
     { getViewer()->GLInvalidateAll(); }
 
-  void addAssistantType(const std::string &name, const std::string &typeName) {
-    const std::wstring localName = tr(name.c_str()).toStdWString();
-    if (m_localnameToType.count(localName)) return;
-    m_localnameToType[localName] = TStringId(typeName);
-    m_assistantType.addValue(localName);
+  TPropertyGroup* getProperties(int) override {
+    m_allProperties.clear();
+    for(int i = 0; i < m_toolProperties.getPropertyCount(); ++i)
+      m_allProperties.bind( *m_toolProperties.getProperty(i) );
+    if (Closer closer = read(ModeAssistant)) {
+      TPropertyGroup &assistantProperties = m_readAssistant->getProperties();
+      for(int i = 0; i < assistantProperties.getPropertyCount(); ++i)
+        m_allProperties.bind( *assistantProperties.getProperty(i) );
+    }
+    updateTranslation();
+    return &m_allProperties;
+  }
+
+  void addAssistantType(const std::string &typeName, const std::string &name) {
+    if (m_typeToName.insert( TypeMap::value_type(TStringId(typeName), name) ).second)
+      m_assistantType.addValueWithUIName(
+        to_wstring(typeName),
+        QString::fromStdString(name) );
   }
 
   void updateTranslation() override {
-    m_assistantType.setQStringName(tr("Assistant Type"));
-    m_assistantType.deleteAllValues();
-    m_localnameToType.clear();
-    addAssistantType("--", "");
-    addAssistantType("Vanishing Point", "assistantVanishingPoint");
-    m_assistantType.setIndex(0);
+    for(TypeMap::const_iterator i = m_typeToName.begin(); i != m_typeToName.end(); ++i)
+      m_assistantType.setItemUIName(
+        to_wstring(i->first.str()),
+        tr(i->second.c_str()) );
+    for(int i = 0; i < m_allProperties.getPropertyCount(); ++i) {
+      TProperty *p = m_allProperties.getProperty(i);
+      //p->setQStringName( tr( p->getUINameOrig().c_str() ) );
+    }
   }
 
-  bool onPropertyChanged(std::string propertyName) override {
-    TypeMap::const_iterator i = m_localnameToType.find(m_assistantType.getValue());
-    m_newAssisnantType = i == m_localnameToType.end() ? TStringId() : i->second;
+  bool onPropertyChanged(std::string name/*, bool addToUndo*/) override {
+    if (TProperty *property = m_toolProperties.getProperty(name)) {
+      if (name == m_assistantType.getName())
+        m_newAssisnantType = TStringId::find( to_string(m_assistantType.getValue()) );
+    } else {
+      if (Closer closer = write(ModeAssistant, true))
+        m_writeAssistant->propertyChanged(TStringId::find(name));
+      //if (addToUndo) apply();
+      getViewer()->GLInvalidateAll();
+    }
     return true;
   }
 
-  void resetCurrentPoint() {
+protected:
+  void close() {
+    m_readAssistant = 0;
+    m_readObject.reset();
+    m_readImage = 0;
+    if (m_reader) delete(m_reader);
+    m_reader = 0;
+
+    m_writeAssistant = 0;
+    m_writeObject.reset();
+    m_writeImage = 0;
+    if (m_writer) delete(m_writer);
+    m_writer = 0;
+  }
+
+  bool openRead(Mode mode) {
+    close();
+
+    if ( (mode >= ModeAssistant && !m_currentAssistant)
+      || (mode >= ModeAssistant && m_currentAssistantIndex < 0)
+      || (mode >= ModePoint && m_currentPointIndex < 0) ) return false;
+
+    m_readImage = dynamic_cast<TMetaImage*>(getImage(true));
+    if (m_readImage) {
+      m_reader = new TMetaImage::Reader(*m_readImage);
+      if (mode == ModeImage) return true;
+
+      if ( m_currentAssistantIndex < (int)(*m_reader)->size()
+        && (**m_reader)[m_currentAssistantIndex] == m_currentAssistant )
+      {
+        m_readObject = (**m_reader)[m_currentAssistantIndex];
+        m_readAssistant = m_readObject->getHandler<TAssistant>();
+        if (mode == ModeAssistant) return true;
+
+        if (m_currentPointIndex < m_readAssistant->pointsCount()) {
+          if (mode == ModePoint) return true;
+        }
+      }
+    }
+
+    close();
+    return false;
+  }
+
+  void touch() {
+    if (m_writeAssistant && !m_currentAssistantChanged) {
+      m_currentAssistantBackup = m_writeAssistant->data();
+      m_currentAssistantChanged = true;
+    }
+  }
+
+  bool openWrite(Mode mode, bool touch = false) {
+    close();
+
+    if ( (mode >= ModeAssistant && !m_currentAssistant)
+      || (mode >= ModeAssistant && m_currentAssistantIndex < 0)
+      || (mode >= ModePoint && m_currentPointIndex < 0) ) return false;
+
+    m_writeImage = dynamic_cast<TMetaImage*>(getImage(true));
+    if (m_writeImage) {
+      m_writer = new TMetaImage::Writer(*m_writeImage);
+      if (mode == ModeImage) return true;
+
+      if ( m_currentAssistantIndex < (int)(*m_writer)->size()
+        && (**m_writer)[m_currentAssistantIndex] == m_currentAssistant )
+      {
+        m_writeObject = (**m_writer)[m_currentAssistantIndex];
+        m_writeAssistant = m_writeObject->getHandler<TAssistant>();
+        if ( (mode == ModeAssistant)
+          || (mode == ModePoint && m_currentPointIndex < m_writeAssistant->pointsCount()) )
+        {
+          if (touch) this->touch();
+          return true;
+        }
+      }
+    }
+
+    close();
+    return false;
+  }
+
+
+  //! helper functions for construction like this:
+  //!   if (Closer closer = read(ModeAssistant)) { do-something... }
+  struct Closer {
+    struct Args {
+      EditAssistantsTool *owner;
+      Args(EditAssistantsTool &owner): owner(&owner) { }
+      operator bool() const //!< declare bool-convertor here to prevent convertion path: Args->Closer->bool
+        { return owner && (owner->m_reader || owner->m_writer); }
+      void close()
+        { if (owner) owner->close(); }
+    };
+    Closer(const Args &args): args(args) { }
+    ~Closer() { args.close(); }
+    operator bool() const { return args; }
+  private:
+    Args args;
+  };
+
+  Closer::Args read(Mode mode)
+    { openRead(mode); return Closer::Args(*this); }
+  Closer::Args write(Mode mode, bool touch = false)
+    { openWrite(mode, touch); return Closer::Args(*this); }
+
+public:
+  void updateOptionsBox()
+    { /* getApplication()->getCurrentTool()->notifyToolOptionsBoxChanged(); */ }
+
+  void resetCurrentPoint(bool updateOptionsBox = true) {
+    close();
+    m_currentAssistant.reset();
     m_currentAssistantCreated = false;
+    m_currentAssistantChanged = false;
     m_currentAssistantIndex = -1;
     m_currentPointIndex = -1;
     m_currentPointOffset = TPointD();
     m_currentAssistantBackup.reset();
+    if (updateOptionsBox) this->updateOptionsBox();
   }
 
-  const TAssistant* findCurrentPoint(const TPointD &position) {
-    resetCurrentPoint();
-    TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(false));
-    if (!mi) return NULL;
-
-    double pixelSize2 = tglGetPixelSize2();
-    TMetaImage::Reader reader(*mi);
-    const TAssistant *currentAssisntant = NULL;
-    for(TMetaObjectRefList::const_iterator i = reader->begin(); i != reader->end(); ++i) {
-      if (!*i) continue;
-      const TAssistant *assistant = (*i)->getHandler<TAssistant>();
-      if (!assistant) continue;
-      assistant->deselectAll();
-      for(int j = 0; j < assistant->pointsCount() && m_currentAssistantIndex < 0; ++j) {
-        const TAssistantPoint &p = assistant->points()[j];
-        TPointD offset = p.position - position;
-        if (norm2(offset) <= p.radius*p.radius*pixelSize2) {
-          m_currentAssistantIndex = i - reader->begin();
-          m_currentPointIndex = j;
-          m_currentPointOffset = offset;
-          currentAssisntant = assistant;
-          assistant->selectPoint(j);
-          break;
+  bool findCurrentPoint(const TPointD &position, double pixelSize = 1, bool updateOptionsBox = true) {
+    resetCurrentPoint(false);
+    if (Closer closer = read(ModeImage)) {
+      for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i) {
+        if (!*i) continue;
+
+        const TAssistant *assistant = (*i)->getHandler<TAssistant>();
+        if (!assistant) continue;
+
+        assistant->deselectAll();
+        for(int j = 0; j < assistant->pointsCount() && m_currentAssistantIndex < 0; ++j) {
+          const TAssistantPoint &p = assistant->points()[j];
+          TPointD offset = p.position - position;
+          if (norm(offset) <= p.radius*pixelSize) {
+            m_currentAssistant.set(*i);
+            m_currentAssistantIndex = i - (*m_reader)->begin();
+            m_currentPointIndex = j;
+            m_currentPointOffset = offset;
+            assistant->selectAll();
+          }
         }
       }
     }
-    return currentAssisntant;
+
+    if (updateOptionsBox) this->updateOptionsBox();
+    return m_currentAssistantIndex >= 0;
   }
 
-  void mouseMove(const TPointD &position, const TMouseEvent&) override {
-    if (m_dragging) return;
-    findCurrentPoint(position);
-    m_currentPosition = position;
-    getViewer()->GLInvalidateAll();
+  bool apply() {
+    bool success = false;
+    if (m_currentAssistantChanged || m_currentAssistantCreated) {
+      if (Closer closer = write(ModePoint)) {
+        m_writeAssistant->fixData();
+        TUndoManager::manager()->add(new EditAssistantsUndo(
+          getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
+          getCurrentFid(),
+          m_currentAssistantCreated,
+          false,
+          m_writeObject,
+          m_currentAssistantBackup ));
+        m_currentAssistantCreated = false;
+        m_currentAssistantChanged = false;
+        success = true;
+      }
+    }
+
+    if (success) {
+      notifyImageChanged();
+      getApplication()->getCurrentTool()->notifyToolChanged();
+      getViewer()->GLInvalidateAll();
+    }
+
+    return success;
   }
 
   void leftButtonDown(const TPointD &position, const TMouseEvent&) override {
+    apply();
     m_dragging = true;
     if (m_newAssisnantType) {
       // create assistant
       resetCurrentPoint();
-      if (TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(true))) {
-        TMetaImage::Writer writer(*mi);
-        TMetaObjectR obj(new TMetaObject(m_newAssisnantType));
-        if (TAssistant *assistant = obj->getHandler<TAssistant>()) {
+      if (Closer closer = write(ModeImage)) {
+        TMetaObjectP object(new TMetaObject(m_newAssisnantType));
+        if (TAssistant *assistant = object->getHandler<TAssistant>()) {
           if (assistant->pointsCount()) {
+            assistant->setDefaults();
             assistant->movePoint(0, position);
             m_currentAssistantCreated = true;
-            m_currentAssistantIndex = (int)writer->size();
+            m_currentAssistantChanged = true;
+            m_currentAssistantIndex = (int)(*m_writer)->size();
+            m_currentAssistant = object;
             m_currentPointIndex = 0;
             m_currentPointOffset = TPointD();
+            m_currentAssistantBackup = assistant->data();
           }
-          writer->push_back(obj);
+          (*m_writer)->push_back(object);
         }
       }
       m_newAssisnantType.reset();
-    } else
-    if (const TAssistant *assistant = findCurrentPoint(position)) {
-      m_currentAssistantBackup = assistant->data();
+    } else {
+      findCurrentPoint(position);
     }
+
     m_currentPosition = position;
     getViewer()->GLInvalidateAll();
   }
 
   void leftButtonDrag(const TPointD &position, const TMouseEvent&) override {
-    if (m_currentAssistantIndex >= 0)
-    if (m_currentPointIndex >= 0)
-    if (TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(true)))
-    {
-      TMetaImage::Writer writer(*mi);
-      if (m_currentAssistantIndex < (int)writer->size())
-      if (TMetaObjectR obj = (*writer)[m_currentAssistantIndex])
-      if (TAssistant *assistant = obj->getHandler<TAssistant>())
-      if (m_currentPointIndex < assistant->pointsCount())
-      {
-        assistant->movePoint(
-          m_currentPointIndex,
-          position + m_currentPointOffset);
-      }
-    }
+    if (Closer closer = write(ModePoint, true))
+      m_writeAssistant->movePoint(
+        m_currentPointIndex,
+        position + m_currentPointOffset);
     m_currentPosition = position;
     getViewer()->GLInvalidateAll();
   }
 
   void leftButtonUp(const TPointD &position, const TMouseEvent&) override {
-    if (m_currentAssistantIndex >= 0)
-    if (m_currentPointIndex >= 0)
-    if (TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(true)))
-    {
-      TMetaImage::Writer writer(*mi);
-      if (m_currentAssistantIndex < (int)writer->size())
-      if (TMetaObjectR obj = (*writer)[m_currentAssistantIndex])
-      if (TAssistant *assistant = obj->getHandler<TAssistant>())
-      if (m_currentPointIndex < assistant->pointsCount())
-      {
-        assistant->movePoint(
-          m_currentPointIndex,
-          position + m_currentPointOffset);
-        assistant->fixData();
-        TUndoManager::manager()->add(new EditAssistantsUndo(
-          getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
-          getCurrentFid(),
-          m_currentAssistantCreated,
-          false,
-          obj,
-          m_currentAssistantBackup ));
-        m_currentAssistantCreated = false;
-      }
-    }
-    notifyImageChanged();
+    if (Closer closer = write(ModePoint, true))
+      m_writeAssistant->movePoint(
+        m_currentPointIndex,
+        position + m_currentPointOffset);
+
+    apply();
     m_assistantType.setIndex(0);
     getApplication()->getCurrentTool()->notifyToolChanged();
+    emit getApplication()->getCurrentTool()->toolChanged();
     m_currentPosition = position;
     getViewer()->GLInvalidateAll();
     m_dragging = false;
   }
 
-  bool keyEvent(
-    bool press,
-    TInputState::Key key,
-    QKeyEvent *event,
-    const TInputManager &manager )
-  {
-    if (key == TKey(Qt::Key_Delete)) {
-      if (!m_dragging)
-      if (m_currentAssistantIndex >= 0)
-      if (TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(true)))
-      {
-        { // wrap writer
-          TMetaImage::Writer writer(*mi);
-          if (m_currentAssistantIndex < (int)writer->size())
-          if (TMetaObjectR obj = (*writer)[m_currentAssistantIndex])
-          if (TAssistant *assistant = obj->getHandler<TAssistant>())
-          {
-            writer->erase(writer->begin() + m_currentAssistantIndex);
-            TUndoManager::manager()->add(new EditAssistantsUndo(
-              getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
-              getCurrentFid(),
-              false,
-              true,
-              obj,
-              obj->data() ));
-          }
+  bool keyDown(QKeyEvent *keyEvent) override {
+    if (keyEvent->key() == Qt::Key_Delete) {
+      if (!m_dragging) {
+        apply();
+        bool success = false;
+        if (Closer closer = write(ModeAssistant, true)) {
+          (*m_writer)->erase((*m_writer)->begin() + m_currentAssistantIndex);
+          TUndoManager::manager()->add(new EditAssistantsUndo(
+            getApplication()->getCurrentLevel()->getLevel()->getSimpleLevel(),
+            getCurrentFid(),
+            false,
+            true,
+            m_writeObject,
+            m_writeObject->data() ));
+          success = true;
         }
+
+        if (success) {
+          notifyImageChanged();
+        }
+
         resetCurrentPoint();
-        notifyImageChanged();
         getApplication()->getCurrentTool()->notifyToolChanged();
         getViewer()->GLInvalidateAll();
       }
@@ -326,10 +478,8 @@ public:
     m_currentGuidelines.clear();
 
     // draw assistants
-    TMetaImage *mi = dynamic_cast<TMetaImage*>(getImage(false));
-    if (!mi) return;
-    TMetaImage::Reader reader(*mi);
-    for(TMetaObjectRefList::const_iterator i = reader->begin(); i != reader->end(); ++i)
+    if (Closer closer = read(ModeImage))
+    for(TMetaObjectListCW::iterator i = (*m_reader)->begin(); i != (*m_reader)->end(); ++i)
       if (*i)
       if (const TAssistant *assistant = (*i)->getHandler<TAssistant>())
       {