]> matita.cs.unibo.it Git - helm.git/blobdiff - helm/DEVEL/mathml_editor/src/TPushParser.cc
* added show/hide cursro methods
[helm.git] / helm / DEVEL / mathml_editor / src / TPushParser.cc
index ea82cf379251858d2f9536d56369d98fe513701b..a3fb9772996a11becbd03e578b62b6badac40ca2 100644 (file)
@@ -1,29 +1,40 @@
 
+#include "ALogger.hh"
 #include "TPushParser.hh"
-#include "TListener.hh"
+#include "AMathMLFactory.hh"
 
-TPushParser::TPushParser(const TDictionary& d) : dictionary(d), listener(0)
+TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d)
 {
   init();
 }
 
-TPushParser::TPushParser(const TDictionary& d, TListener& l) : dictionary(d), listener(&l)
+TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d)
 {
   init();
 }
 
+TPushParser::~TPushParser()
+{
+}
+
 void
 TPushParser::init()
 {
-  nextId = 1;
   cursor = doc.create("cursor");
-  cursor["id"] = "I0";
-  doc.clearDirty();
-  doc.root().append(cursor);
+  cursor["visible"] = "1";
+  hiddenCursor = 0;
+  reset();
 }
 
-TPushParser::~TPushParser()
+void
+TPushParser::reset()
 {
+  cout << "passo di qui dentro pushparser " << factory << endl;
+  nextId = 1;
+  if (cursor.parent()) cursor.remove();
+  doc.reset();
+  doc.root().append(cursor);
+  if (factory) factory->documentModified(doc);
 }
 
 std::string
@@ -56,6 +67,65 @@ TPushParser::do_begin()
     }
 }
 
+bool
+TPushParser::correctBrace()
+{
+  // this method MUST be invoked when the cursor is child of a 
+  // phantom group, which in turn is the last rightOpen MACRO's child.
+  // The only way to exit from a rightOpen MACRO is opening a group before 
+  // inserting the MACRO and, once the MACRO is completely inserted, closing 
+  // the group.
+  // This method return true if the condition above is true. False, otherwise.
+  assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
+  TNode parent = cursor.parent();
+  assert(parent.parent() && parent.parent().isC());
+  assert(!frames.empty());
+  Frame& frame = frames.top();
+  assert(frame.entry.rightOpen);
+  assert(parent.parent().last() == parent);
+  
+  TNode c = parent.parent();
+  bool stop = false;
+  bool ok = false;
+  TNode node = c.parent();
+  do
+    {
+      if (node.isG() && node.hasId())
+        {
+         // in this case, the rightOpen MACRO is a child of a group with id. 
+         // So, the '}' is correct
+         ok = true;
+         stop = true;
+       }
+      else if (node.isG())
+        {
+         // the MACRO is a phantom group's child. We have to control why we 
+         // have this phantom group
+         TNode nodeParent = node.parent();
+         if (nodeParent && nodeParent.isC())
+           {
+             // we have to control the nature of this MACRO
+             const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
+             if (entry.rightOpen && node == nodeParent.last())
+               {
+                 // in this case we have to re-iterate the process
+                 node = nodeParent.parent();
+               }
+             else stop = true;
+           }
+         else stop = true;
+       }
+      else
+        {
+         // at the moment we assume that a MACRO cannot be child of an element other than a group
+         stop = true;
+       }
+    }
+  while (!stop);
+
+  return ok;
+}
+
 void
 TPushParser::do_end()
 {
@@ -68,25 +138,105 @@ TPushParser::do_end()
     }
   else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
     {
+      assert(!frames.empty());
+      // closing brace for a structure in which & or \cr have been used
+      TNode row = parent.parent().parent();
+      assert(row && row.is("row"));
+      assert(row.parent());
+      advance(row);
+    }
+  else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
+    {
+      // In this case, we have to control the cursor's grand parent.
+      TNode gparent = parent.parent();
+
+      if (gparent.isC() && gparent.last() == parent)
+        {
+         // a frame MUST be in the stack
+         assert(!frames.empty());
+
+         // we have to control the nature of this macro
+         if (frames.top().entry.rightOpen)
+           {
+             // in this case, the '}' character is the proper way to exit from the phantom group, and 
+             // in particular, this character means that the user wants to exit from the MACRO.
+             // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this 
+             // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could 
+             // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control 
+             // is done by the correctBrace method.
+             if (!correctBrace())
+               {
+                 // the '}' is not correct
+                 logger.warning("nothing to close");
+               }
+             else
+               {
+                 cursor.remove();
+                 advance(parent);
+               }
+             
+           }
+         else
+           {
+             logger.error("closing brace ignored");
+           }
+       }
+      else
+        {
+         // at the moment, a phantom group with cursor can be a MACRO's child or a cell's child, and these cases
+         // are handled in other blocks of code.
+         logger.error("do_end: strange TML tree");
+       }
+    }
+  else
+    {
+      // In this case, there is a redundant '}', so we can ignore it and 
+      // emit an error
+      logger.warning("There is so no corresponding'{'");
+      //assert(0);
+    }
+}
+
+
+/*
+void
+TPushParser::do_end()
+{
+  TNode parent = cursor.parent();
+  if (parent && parent.isG() && parent.hasId())
+    {
+      // normal closing brace for an explicitly open group
+      cursor.remove();
+      advance(parent);
+    }
+  else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
+    {
+      assert(!frames.empty());
       // closing brace for a structure in which & or \cr have been used
       TNode row = parent.parent().parent();
       assert(row && row.is("row"));
       TNode table = row.parent();
       assert(table);
-      advance(table);
+      advance(row);
     }
-  else if (parent && parent.isG() && !parent.hasId() && parent.parent())
+  else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
     {
       // closing brace for a right-open macro (like \over)
+      // It's not sure that we want to exit from a phantom group with a 
+      // '}', because, to enter in the phantom group the user didn't insert a 
+      // '{'.
       cursor.remove();
-      advance(parent.parent());
+      advance(parent);
     }
   else
     {
-      // ???
-      assert(0);
+      // In this case, there is a redundant '}', so I think we can ignore it and 
+      // emit an error
+      logger.warning("There is so no corresponding'{'");
+      //assert(0);
     }
 }
+*/
 
 void
 TPushParser::do_shift()
@@ -115,6 +265,7 @@ TPushParser::do_shift()
          else
            {
              // we need two closing math shifts
+             //cursor.remove(); ??
              parent.parent().append(cursor);
            }
        }
@@ -135,7 +286,7 @@ TPushParser::do_shift()
     }
   else
     {
-      cerr << "ERROR: math shift" << endl;
+      logger.error("parser: math shift");
     }
 }
 
@@ -169,7 +320,7 @@ TPushParser::do_align()
     }
   else
     {
-      cerr << "alignment tab used outside matrix" << endl;
+      logger.error("alignment tab used outside matrix");
     }
 }
 
@@ -210,7 +361,7 @@ TPushParser::do_subscript()
     }
   else if (parent.isSb() && cursor == parent[1])
     {
-      if (parent["under"] == "1") cerr << "already under" << endl;
+      if (parent["under"] == "1") logger.error("already under");
       else parent["under"] = "1";
     }
 }
@@ -240,7 +391,7 @@ TPushParser::do_superscript()
     }
   else if (parent.isSp() && cursor == parent[1])
     {
-      if (parent["over"] == "1") cerr << "already over" << endl;
+      if (parent["over"] == "1") logger.error("already over");
       else parent["over"] = "1";
     }
 }
@@ -248,13 +399,13 @@ TPushParser::do_superscript()
 void
 TPushParser::do_space(const std::string&)
 {
-  // ? may be used to distinguish tokens in some mode?
+  logger.debug("do_space");
 }
 
 void
 TPushParser::do_letter(const std::string& s)
 {
-  TNode parent = cursor.parent();
+  //TNode parent = cursor.parent();
   TNode elem = doc.createI(s, nextId++);
   cursor.replace(elem);
   advance(elem);
@@ -310,7 +461,8 @@ TPushParser::do_apostrophe()
        }
       else
        {
-         // error ???
+         // is it an error?
+         logger.error("parser: you have to type one identifier before the  ''");
        }
     }
   else
@@ -328,8 +480,8 @@ TPushParser::do_other(const std::string& s)
       do_apostrophe();
       break;
     default:
-      cout << "TPushParser::do_other " << s << endl;
-      cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;
+      /*cout << "TPushParser::do_other " << s << endl;
+      cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
       TNode elem = doc.createT("o", s, nextId++);
       cursor.replace(elem);
       advance(elem);  
@@ -410,6 +562,7 @@ TPushParser::do_control(const std::string& name)
          {
            TNode m = doc.createC(name, nextId++);
            cursor.replace(m);
+           
            if (entry.leftOpen && entry.rightOpen)
              {
                assert(entry.pattern.empty());
@@ -446,6 +599,7 @@ TPushParser::do_control(const std::string& name)
                if (parent.isG())
                  {
                    frames.push(Frame(entry));
+
                    if (entry.paramDelimited(0))
                      {
                        TNode g = doc.createG();
@@ -458,7 +612,7 @@ TPushParser::do_control(const std::string& name)
                else
                  {
                    // error, but we could handle this very easily
-                   cerr << "error, but we could handle this easily" << endl;
+                   logger.error(" parser:but we could handle this easily");
                  }
              }
            else advance(m);
@@ -466,7 +620,7 @@ TPushParser::do_control(const std::string& name)
          break;
        case TDictionary::UNDEFINED:
          {
-           cerr << "ERROR: using undefined macro `" << name << "'" << endl;
+           logger.error("parser: using undefined macro " + name);
            TNode m = doc.createC(name, nextId++);
            cursor.replace(m);
            advance(m);
@@ -478,6 +632,745 @@ TPushParser::do_control(const std::string& name)
     }
 }
 
+void
+TPushParser::gdelete_prev_token()
+{
+  assert(cursor.prev());
+  assert(cursor.parent());
+  TNode prev = cursor.prev();
+  assert(prev.is("i") || prev.is("o") || prev.is("n"));
+  
+  // the control below is designed to handle the case in which val have more than one unicode character
+  DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
+  if ((prev.is("i")) || (ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
+    {
+      cursor.remove();
+      prev.replace(cursor);
+      
+      if (cursor.parent().isC())
+        {
+         // in this case we have removed an element of a MACRO. 
+         // we can assert that this element was a non delimited argument
+         assert(!frames.empty());
+         Frame& frame = frames.top();
+         assert(frame.pos > 0);
+
+         frame.pos--;
+       }
+    }
+  else
+    {
+      ucs4val.erase(ucs4val.length() - 1, 1);
+      prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
+    }
+
+}
+
+void
+TPushParser::gdelete_prev_script()
+{
+  // this method deletes an sp or an sb preceding the cursor
+  assert(cursor.prev());
+  assert(cursor.parent());
+  TNode prev = cursor.prev();
+  assert(prev.is("sp") || prev.is("sb"));
+  cursor.remove();
+  prev.append(cursor);
+  // we can invoke the gdelete_prev, because a sp (sb) MUST have two children
+  gdelete_prev();
+}
+
+void
+TPushParser::gdelete_prev_group()
+{
+  assert(cursor.prev() && cursor.prev().isG());
+  TNode prev = cursor.prev();
+  cursor.remove();
+  prev.append(cursor);
+  
+  // a group could have no children, so the gdelete_prev is not appropriate
+  // so, this method is not equivalent to the one above
+  do_gdelete();
+}
+
+void
+TPushParser::gdelete_prev_macro()
+{
+  assert(cursor.parent());
+  assert(cursor.prev());
+  TNode prev = cursor.prev();
+  assert(prev.isC());
+  
+  const TDictionary::Entry& entry = dictionary.find(prev["name"]);
+  
+  if (!entry.defined())
+    {
+      // We can assume that the user want to completely delete the undefined macro
+      cursor.remove();
+      prev.replace(cursor);
+    }
+  else
+    {   
+      // we start to remove a MACRO. Different actions must be taken, based on the nature 
+      // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
+      // cases it's correct. In the first set of cases, we have to update the stack, pushing
+      // a frame in it with a correct value of pos, in the 
+      // second one, we must not push a frame in the stack
+      
+      if (entry.rightOpen)
+        {
+         // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
+         // if the control element is rightOpen, the cursor should be placed after 
+         // the last child of the control element's last child, and than, we try to remove something.
+         // A frame MUST be pushed in the stack, because we dont' know if the following actions 
+         // will completely remove the MACRO.
+         frames.push(Frame(entry));
+
+         // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
+         assert(prev.last().isG() && !prev.last().hasId());
+         
+         cursor.remove();
+         prev.last().append(cursor);
+         
+         // the gdelete_prev is not appropriate, because the last child of the MACRO could have no children
+         do_gdelete_phantom_group();
+       }
+      else if (entry.leftOpen)
+        {
+         // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
+         // In this case, we do not have to push a frame in the stack, because we remove the 
+         // MACRO immediately, substituting it with the content of the phantom group.
+         // At the moment, we don't remove the last child of the phantom group, but
+         // it's not clear if it's the correct behavior of the graphical deleting.
+         // To delete it, just remove the comment of the last instruction of the 
+         // if (g.size()) block.
+         assert(prev.first());
+         assert(prev.first().isG());
+         assert(prev.first() == prev.last());
+         
+         TNode g = prev.first();
+         if (g.size())
+           {
+             // in this case, the phantom group has at least one child, so we can call the 
+             // TNode::replace.
+             g.remove();
+             prev.replace(g.first(), TNode());
+             //gdelete_prev();
+           }
+         else
+           {
+             // otherwise, the phantom group has no children, so we remove it, also the MACRO.
+             cursor.remove();
+             g.remove();
+             prev.replace(cursor);
+           }
+       }
+      else if (!entry.pattern.empty())
+        {
+         // we have to start to remove a MACRO which accepts arguments.
+         // If the MACRO accepts arguments, the MACRO has at least one child     
+         assert(prev.size() >= 1);
+
+         // Differnt actions must be taken, based on the nature of the last child
+         // of the MACRO. We have to distinguish the case in which it's a delimited argument,
+         // frome the one in which it's a not delimited argument.
+         if (prev.last().isG() && !prev.last().hasId())
+           {
+             // the last argument of the MACRO is a delimited argumet. We ideally remove 
+             // the sequence of delimiters
+             cursor.remove();
+             prev.last().append(cursor);
+             //  we have to push a frame with a correct value of pos
+             assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
+             
+             unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
+             unsigned p = entry.pattern.size() - sequence_length - 1;
+             // now, p is the correct value of pos, and we can push the frame.
+             frames.push(Frame(entry, p));
+           }
+         else
+           {
+             // in this case, the last child of the MACRO is not a delimited argument, so we try 
+             // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not. 
+             cursor.remove();
+             if (entry.table == 1 && prev.last().is("row"))
+               {
+                 // in this case the cursor should be appended to the group associated to 
+                 // the last cell of the last row of the table
+                 assert(prev.last().last().is("cell") && prev.last().last().first().isG());
+                 prev.last().last().first().append(cursor);
+               }
+             else
+               {
+                 prev.append(cursor);
+               }
+
+             // we push a frame in the stack with a correct value of member pos.
+             // This correct value is the size of the pattern, because we have to START to delete 
+             // a MACRO. It means that all of the MACRO's arguments have been inserted.
+             frames.push(Frame(entry, entry.pattern.size()));
+
+             if (cursor.prev())
+               {
+                 // in this case we try to remove the last child of the MACRO.
+                 gdelete_prev();
+               }
+             else
+               {
+                 // this block of code will never be executed, because the MACRO accepts arguments, 
+                 // so a MACRO's child MUST exist. But, we can handle this case, emitting a warning
+                 logger.warning("Parser: the TML tree is in a strange state, but a good state will be generated");
+                 do_gdelete();
+               }
+           } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
+
+       } // end of if (!entry.pattern.empty())
+      else
+        {
+         // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
+         // and has no pattern. It means that it has no children.
+         // We can replace it with the cursor
+         assert(prev.size() == 0);
+         cursor.remove();
+         prev.replace(cursor);
+       }
+    } // end of defined MACRO
+
+}
+
+void
+TPushParser::gdelete_prev()
+{
+  // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
+  
+  assert(cursor.prev());
+  assert(cursor.parent());
+
+  TNode prev = cursor.prev();
+
+  if (prev.is("i") || prev.is("o") || prev.is("n"))
+    {
+      gdelete_prev_token();
+    }
+  else if (prev.isSp() || prev.isSb())
+    {
+      gdelete_prev_script();
+    }
+  else if (prev.isG())
+    {
+      gdelete_prev_group();
+    }
+  else if (prev.isC())
+    {
+      // here, we also treat the case in which the MACRO is a table
+      gdelete_prev_macro();
+    }
+  else 
+    {
+      // not handled. Future cases...
+    }
+
+} // end of method
+
+void
+TPushParser::rgreplace_father(void)
+{
+  // this method MUST only be invoked, when the cursor
+  // is the only child of a group with id. This function 
+  // replaces the group with the cursor. But if the new parent
+  // is a group with id and the cursor is the only child of the 
+  // group, the new parent is replaced...and so on.
+  // r stands for recursive, g stands for graphical.
+  assert(cursor.parent());
+  assert(cursor.parent().isG() && cursor.parent().hasId());
+
+  TNode parent = cursor.parent();
+
+  while (parent.isG() && parent.hasId() && (parent.first() == cursor))
+    {
+      parent.replace(cursor);
+      parent = cursor.parent();
+    }
+
+  if (parent.isC())
+    {
+      // in this case we have removed a MACRO's child. 
+      // So, we have to update the member pos of the frame in the stack
+      // We can assert that this MACRO accepts arguments.
+      assert(!frames.empty());
+      Frame& frame = frames.top();
+      assert(frame.pos > 0);
+      frame.pos--;
+    }
+}
+
+void
+TPushParser::do_gdelete_script()
+{
+  // If we are here, the cursor is child of a script (sp or sb) and 
+  // this means that a prev does exist and that there is one and only one 
+  // element preceding the cursor. The sp's (or sb's) parent 
+  // MUST NOT be a MACRO.
+  // The element preceding the cursor is the base of the script.
+
+  assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
+  TNode parent = cursor.parent();
+         
+  assert(parent.size() == 2);
+  assert(parent.parent() && !parent.parent().isC());
+  
+  TNode prev = cursor.prev();
+  cursor.remove();
+  if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
+    {
+      // in this case, the script's base is a group with no elements, so 
+      // we have to remove the entire MACRO, replacing it with the cursor.
+      // This situation occurs when the user type something like this
+      //   $....{}^
+      // or this 
+      //   $^
+      // or this
+      //   $...{^
+      prev.remove();
+      parent.replace(cursor);
+      
+      // if the new parent is a group with Id and the cursor is the only 
+      // element of this group, we have to remove the group. These controls are made
+      // in the method rgreplace_father().
+      if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_father();
+    }
+  else
+    {
+      // in this case, the prev has to replace the script, 
+      // and the cursor has to be placed after the prev. 
+      assert(prev.hasId());
+      parent.replace(prev);
+      prev.parent().append(cursor);
+      // now prev have a preceding element
+      assert(cursor.parent().size() > 1);
+    }
+  
+} // end of method do_gdelete_script
+
+void
+TPushParser::do_gdelete_macro()
+{
+  // If we are here, the cursor is a child of a MACRO and this means
+  // that there is an open frame for the control element
+  // and this element is closed at either side (no leftOpen no rightOpen)
+  // and the MACRO is waiting for a not delimited argument, so 
+  // we can assert that frame.entry.pattern.size() >= 1
+  assert(cursor.parent() && cursor.parent().isC());
+  TNode parent = cursor.parent();
+  
+  assert(!frames.empty());
+  Frame& frame = frames.top();
+  assert(frame.entry.pattern.size() >= 1);
+
+  // we have to take different actions, based on if a preceding element exists 
+  // or not
+  TNode prev = cursor.prev();
+  if (!prev)
+    {
+      // in this case, a prev does not exist, so the actions of deleting means 
+      // that we have to remove the MACRO. So we have to pop the stack.
+      // Being here also means that the MACRO is waiting for the first argument
+      // (which is not delimited), but we don't mind about it.
+      assert(frame.pos == 0);
+      parent.replace(cursor);
+      frames.pop();
+
+      // if the new parent is a group with Id, and has no elements other than the 
+      // cursor, we can remove it, because it' a graphical deleting
+      if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
+       rgreplace_father();
+      else if (cursor.parent().isC())
+        {
+         // We have assumed that a MACRO cannot be a MACRO's child. 
+         // At the moment, this assumption is valid, but in a future 
+         // it might be false.
+         assert(!frames.empty());
+         Frame& frame = frames.top();
+         assert(frame.pos > 0);
+         frame.pos--;
+       }
+    }
+  else
+    {
+      // a prev does exist, we have to control if it's a delimited argument or not.
+      if (prev.isG() && !prev.hasId())
+        {
+         // in this case, prev is a delimited argument, so we have 
+         // to ideally remove the sequence of delimiters
+         Frame& frame = frames.top();
+         assert(frame.pos > 1);
+         cursor.remove();
+         prev.append(cursor);
+         assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
+         
+         // these 3 lines of code update the member pos.
+         unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
+         assert(sequence_length);
+         frame.pos = frame.pos - sequence_length - 1;
+       }
+      else
+        {
+         // the prev is not a delimited argument, so we have to try to remove it. 
+         // We "try", because the prev might be a something that 
+         // a simple delete cannot remove completely
+         gdelete_prev();
+       }
+    }
+
+}
+
+void
+TPushParser::do_gdelete_groupId()
+{
+  // if we are here, the cursor's parent is a group with Id
+  assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
+  TNode parent = cursor.parent();
+
+  // we have to take different actions based on if the cursor has a preceding 
+  // element or not
+  TNode prev = cursor.prev();
+  if (prev)
+    {
+      // the cursor has a preceding element, so we try to remove it
+      gdelete_prev();
+
+      // We control if the group has to be removed, because the cursor 
+      // might be the only element of the group.
+      if ((parent.first() == cursor) && parent.isG() && parent.hasId())
+       rgreplace_father();
+      
+    }
+  else
+    {
+      // the cursor has no preceding elements, so we have to remove the 
+      // group.
+      rgreplace_father();
+      
+      // we have to re-start the process, because it' a graphical delete
+      do_gdelete();
+    }
+
+} // end of method do_gdelete_groupId()
+
+void
+TPushParser::do_gdelete_phantom_group()
+{
+  // if we are here, the cursor MUST be a child of a 
+  // phantom group.
+  assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
+
+  TNode parent = cursor.parent();
+
+  // now we have to control if the cursor has a preceding element or not
+  TNode prev = cursor.prev();
+  if (prev)
+    {
+      // the cursor has a preceding element, so we try to remove it
+      gdelete_prev();
+
+      if (parent.size() == 1 && parent.parent().isSp())
+        {
+         // in this case the gdelete_prev has removed the only element preceding the cursor.
+         // If the phantom group is an sp's child, it means that the user has removed all \' in the 
+         // phantom group.
+         // We can remove the phamtom group and the sp element. But we also can only remove the 
+         // phantom group, giving the user the possibility of inserting an exponent.
+         // At the moment, we remove the sp element (and the phantom group), because it may be the correct 
+         // behavior of a graphical deleting.
+         cursor.remove();
+         parent.replace(cursor);
+         // now we have an sp element with two children: the first child (we don't know anything about it)
+         // and the cursror.
+         assert(cursor.parent().size() == 2);
+         
+         // to delete the script we can invoke the do_gdelete_script(), which will do all controls we need.
+         // To give the possibility of insetring an exponent, just remove the following istruction.
+         do_gdelete_script();
+       }
+      else if (parent.parent().isSp())
+        {
+         // in this case we have to place the cursor after the sp element
+         cursor.remove();
+         assert(parent.parent().parent());
+         parent.parent().parent().append(cursor);
+       }
+    }
+  else
+    {
+      // in this case the cursor is the only element of the phantom group,
+      // so we have to remove it. But, a phantom group has a special role, 
+      // so we have to control the grand father of the cursor.
+      TNode gfather = parent.parent();
+      if (!gfather)
+        {
+         // If here, the TML tree is in an inconsistent state
+         logger.error("do_gdelete_phantom: TML tree in a inconsistent state");
+       }
+      else if (gfather.isC())
+        {
+         // in this case the phantom group is child of a MACRO.
+         // We have to control the nature of this MACRO.
+         assert(!frames.empty());
+          Frame& frame = frames.top();
+         
+         if (frame.entry.leftOpen && frame.entry.rightOpen)
+           {
+             // in this case, the cursor'parent is in the second and last child 
+             // of the MACRO. We can assert that the grand father has two 
+             // children, which are both phantom groups
+             assert(gfather.size() == 2);
+             assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
+             assert(frame.pos == 0);
+             
+             TNode ggfather = gfather.parent();
+             assert(ggfather);
+             cursor.remove();
+             parent.remove();
+             // we have to replace the gfather with the elements of its first child, but this group may have no 
+             // children.
+             if (gfather.first().size())
+               {
+                 gfather.replace(gfather.first().first(), TNode());
+                 ggfather.append(cursor);
+               }
+             else
+               {
+                 // in this case, the MACRO has to be replaced with the cursor
+                 gfather.first().remove();
+                 gfather.replace(cursor);
+               }
+             // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
+             // this MACRO no longer exists.
+             frames.pop();
+           }
+         else if (frame.entry.rightOpen)
+           {
+             // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the 
+             // phantom group), so we remove the MACRO. 
+             // We can assert that cursor's parent is the only child of the MACRO
+             assert(gfather.size() == 1);
+             assert(frame.pos == 0);
+             cursor.remove();
+             parent.remove();
+             gfather.replace(cursor);
+             
+             // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
+             frames.pop();
+           }
+         else if (frame.entry.leftOpen)
+           {
+             // this situation will never occur.
+             logger.error("the parser has generated a wrong TML tree");
+           }
+         else if (!frame.entry.pattern.empty())
+           {
+             // the MACRO accepts arguments, and the phantom group in which 
+             // the cursor is, rappresents a delimited argument
+             // We have to control if the cursor's parent has a preceding element, 
+             // or not.
+             TNode uncle = parent.prev();
+             if (!uncle)
+               {
+                 // the parent is the only element of the MACRO. 
+                 // we can assert that frame.pos == 0.
+                 // In this case we can replace the MACRO with the cursor
+                 assert(frame.pos == 0);
+                 cursor.remove();
+                 parent.remove();
+                 gfather.replace(cursor);
+                 frames.pop();
+               }
+             else
+               {
+                 // the parent has a preceding element. Now we have 
+                 // to control if the uncle is a delimited argument or not.
+                 if (uncle.isG() && !uncle.hasId())
+                   {
+                     // the  uncle is a delimited argument. So we have to ideally
+                     // remove the sequence of delimiters.
+                     assert(frame.pos > 1);
+                     unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
+                     assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
+                     assert(sequence_length);
+                     // sequence_length is the length of the delimiters sequence which separates
+                     // the current parameter and the previous parameter
+                     frame.pos = frame.pos - sequence_length - 1;
+                     cursor.remove();
+                     parent.remove();
+                     uncle.append(cursor);
+                   }
+                 else
+                   {
+                     // the uncle is a not delimited argument, so we try to remove it.
+                     cursor.remove();
+                     parent.replace(cursor);
+                     parent = cursor.parent(); // i update the parent (it should be the MACRO)
+                     assert(parent.isC());
+
+                     // now we try to remove the uncle (now it' the preceding element)
+                     gdelete_prev();
+                   }
+               } // this is the else's end, that handles the case in which an uncle exists
+           } // end of if (!frame.entry.pattern.empty())
+         else
+           {
+             // the entry has no arguments, is not rightOpen and is not leftOpen.
+             logger.warning("parser: TML tree in a strange state, but we try to recover from it");
+             if (gfather.size() == 1)
+               {
+                 cursor.remove();
+                 parent.remove();
+                 gfather.replace(cursor);
+               }
+             else
+               {
+                 logger.warning("parser: TML tree in a very strange state, but we try to recover from it");
+                 cursor.remove();
+                 gfather.replace(cursor);
+               }
+           }
+       } // end of if (gfather.isC())
+      else if (gfather.is("cell"))
+        {
+         // A table is a control sequence, so there is a frame in the stack
+         assert(!frames.empty());
+         assert(frames.top().pos == 1);
+         assert(frames.top().entry.table == 1);
+         
+         // a cell MUST be a row's child, which in turn is a table's child 
+         assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
+         TNode row = gfather.parent();
+         
+         // in this case the cell has no element, so the user wants to delete this cell.
+         TNode prev_cell = gfather.prev();
+         cursor.remove();
+         parent.remove();
+         gfather.remove();
+         // now the cell no longer exists
+
+         if (!prev_cell)
+           {
+             // in this case, the cell is the only cell in the row.
+             // So, we assume that the user wants to delete the entire row.
+             TNode table = row.parent();
+             TNode prev_row = row.prev();
+             row.remove();
+             
+             if (!prev_row)
+               {
+                 // the row was the only child of the table. 
+                 // so we have to delete the entire table
+                 assert(table.parent());
+                 TNode parent_table = table.parent();
+                 table.remove();
+                 frames.pop();
+                 parent_table.append(cursor);
+               }
+             else
+               {
+                 // there are other rows (one or more)
+                 assert(prev_row.is("row"));
+                 assert(prev_row.last());
+                 TNode last_cell = prev_row.last();
+                 assert(last_cell.is("cell"));
+                 assert(last_cell.size() == 1);
+                 assert(last_cell.first().isG() && !last_cell.first().hasId());
+                 last_cell.first().append(cursor);
+               }
+           } // end of if (!prev_cell)
+         else
+           {
+             // being here means that there is a previous cell,
+             // so we append the cursor to group.
+             assert(prev_cell.size() == 1);
+             assert(prev_cell.first().isG() && !prev_cell.first().hasId());
+             prev_cell.first().append(cursor);
+           }
+       } // end of if (gfather.is("cell"))
+      else if (gfather.isSp())
+        {
+         // in this case, the user typed a \'. So this phantom group 
+         // contained a sequence of \'. 
+         // Maybe in this part will never be used, because, if we delete last \' in the 
+         // phantom group, we remove the phantom group also
+         //
+         // In any case, if we are here we have two possibilities: 
+         //   we can delete the phantom group;
+         //   we can delete the superscript.
+         // At the moment we implement the first solution. To implement the second one, just remove 
+         // the line code after the logger.warning and remove comments from the remaining lines
+         logger.warning("parser: TML tree in a strange state, we try to recover from it");
+         parent.replace(cursor);
+         
+         //cursor.remove();
+         //parent.remove();
+         //gfather.replace(cursor);
+       }
+      else if (gfather.is("math"))
+        {
+         // in this case we ignore the user's will of deleting
+         // but we can decide to remove the math mode.
+         logger.info("Parser: nothing to delete");
+       }
+      else
+        {
+         // cursor's grand father is undefined
+         logger.error("parser: TML tree is in a unknown state");
+       }
+    } // end of the else of the if (prev)
+
+}
+
+
+void
+TPushParser::do_gdelete()
+{
+  // we have to handle the case in wich the cursor has a parent or not
+  if (!cursor.parent())
+    {
+      // it's not a good situation...at the moment we do not take actions
+      logger.error("TML tree not well structured");
+    }
+  else
+    {
+      // a parent exists. We have to take differnt actions, based on the nature of 
+      // the parent
+      TNode parent = cursor.parent();
+      if (parent.is("math"))
+        {
+         // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
+         // At the moment, the user's will of deleting is simply ignored
+         logger.warning("nothing to delete");
+       }
+      else if (parent.isG())
+        {
+         // the cursor's parent is a group. We have to control if it's a phantom group or not
+         if (parent.hasId())
+           {
+             do_gdelete_groupId();
+           }
+         else
+           {
+             do_gdelete_phantom_group();
+           }
+       } // end of parent is group
+      else if (parent.isC())
+        {
+         do_gdelete_macro();
+       } // end of parent is a MACRO
+      else if (parent.isSp() || parent.isSb())
+        {
+         do_gdelete_script();
+       } // end of parent is sp or sb
+    } // end of the else which consider the case in which parent exists
+  
+} // end of method do_gdelete
+
 void
 TPushParser::process(const TToken& token)
 {
@@ -504,10 +1397,10 @@ TPushParser::process(const TToken& token)
 void
 TPushParser::push(const TToken& token)
 {
-  cerr << "TPushParser::push " << token.value << " (cat: " << token.category << ")" << endl;
   TNode parent = cursor.parent();
   // If the cursor has no parent then it is detached from the editing
   // tree, which means this token will be ignored
+
   if (parent)
     // If the parent is a phantom group and the grand-parent is a
     // control sequence, there are two cases:
@@ -527,38 +1420,54 @@ TPushParser::push(const TToken& token)
            assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
            if (frame.entry.pattern[frame.pos + 1] == token)
              {
-               // The token matches with the delimiter of the argument.
-               cursor.remove();
-
-               // If the phantom group has just one child, it is not necessary
-               // and can be removed. However, this would complicate
-               // all the code that follows, so given it is a rare case we
-               // leave it alone
-               // if (parent.size() == 1) parent.replace(parent[0]);
-
-               // Eat both the argument and its delimiter
-               frame.pos += 2;
-               if (frame.pos == frame.entry.pattern.size())
-                 {
-                   // This token has completed the entry
-                   advance(parent.parent());
-                 }
-               else if (frame.entry.paramDelimited(frame.pos))
+               // The token matches with a delimiter of the argument, 
+               // hence we increment the frame.pos
+               frame.pos++;
+
+               if (frame.entry.lastDelimiter(frame.pos))
                  {
-                   // For the next is a delimited argument we have to place
-                   // a suitable phantom group with the cursor inside
-                   TNode g = doc.createG();
-                   parent.parent().append(g);
-                   g.append(cursor);
+                   // this delimiter is the last one for the argumet, 
+                   // so the argument is completed
+                   cursor.remove();
+                   frame.pos++;
+
+                   if (frame.pos == frame.entry.pattern.size())
+                     {
+                       // This token has completed the entry
+                       advance(parent);
+                     }
+                   else if (frame.entry.paramDelimited(frame.pos))
+                     {
+                       // For the next is a delimited argument we have to place
+                       // a suitable phantom group with the cursor inside
+                       TNode g = doc.createG();
+                       parent.parent().append(g);
+                       g.append(cursor);
+                     }
+                   else
+                     parent.parent().append(cursor);
                  }
-               else
-                 parent.parent().append(cursor);
              }
            else
              {
-               // Delimiter mismatch. Since we are parsing a delimited
-               // argument we just process the token
-               process(token);
+               // Delimiter mismatch.
+               if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
+                 {
+                   // in this case, there is a sequence of delimiters that delimitates
+                   // the argument, and the user has correctly inserted a portion of this 
+                   // sequence, but now has inserted a wrong delimiter.
+                   // Here, there are some possibilities:
+                   //   - ignore the token, and wait for the correct delimiter
+                   //   - ignore the token, wait for the correct delimiter and emit an error
+                   // At the moment, we implement the second possibily
+                   logger.error("parser: it's not the correct delimiter...you have to type " + frame.entry.pattern[frame.pos + 1].value);
+                 }
+               else
+                 {
+                   // in this case, the sequence of delimiters is composed of one 
+                   // delimiter. It means that we have to process the token
+                   process(token);
+                 }
              }
          }
        else
@@ -577,6 +1486,7 @@ TPushParser::push(const TToken& token)
        // or a fixed token
        Frame& frame = frames.top();
        assert(frame.pos < frame.entry.pattern.size());
+
        if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
          {
            // As by the TeX parsing rules of undelimited parameters,
@@ -611,7 +1521,7 @@ TPushParser::push(const TToken& token)
        else
          {
            // There is a mismatch. Emit an error and ignore the token?
-           cerr << "ERROR: token ignored: " << token.value << " (cat: " << token.category << ")" << endl;
+           logger.debug("parser: token ignored: " + token.value);
          }
 
       }
@@ -619,12 +1529,127 @@ TPushParser::push(const TToken& token)
       process(token);
   else
     {
-      cout << "ignored token" << endl;
+      logger.warning("ignored token");
     }
 
-  if (listener) listener->callback(doc);
+  if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
 }
 
+std::string
+TPushParser::drop()
+{
+  do_gdelete();
+  if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+  return "";
+}
+
+
+void
+TPushParser::advance(const TNode& node)
+{
+  assert(node);
+  
+  if (!node.parent())
+    {
+      // this is an error
+      logger.error("wrong TML tree");
+    }
+  else if (node.parent().isG())
+    {
+      TNode next = node.next();
+      if (next) next.insert(cursor);
+      else node.parent().append(cursor);
+    }
+  else if (node.parent().isC())
+    {
+      assert(!frames.empty());
+      if (frames.top().pos == frames.top().entry.pattern.size())
+        {
+         if (frames.top().entry.rightOpen)
+           {
+             // we have to remove the frame from the stack
+             frames.pop();
+             advance(node.parent().parent());
+           }
+         else
+           {
+             frames.pop();
+             advance(node.parent());
+           }
+       }
+      else if (frames.top().entry.paramDelimited(frames.top().pos))
+        {
+         // the next argument is delimited, so we have to create a phantom group
+         TNode g = doc.createG();
+         g.append(cursor);
+         node.parent().append(g);
+       }
+      else
+        {
+         // the next argumet is not delimited, so we have to append the cursor
+         // to the MACRO
+         node.parent().append(cursor);
+       }
+    }
+  else advance(node.parent());
+}
+
+
+/*
+ * This version handles the case in which we have to 
+ * create a delimited argument
+
+void
+TPushParser::advance(const TNode& node)
+{
+  assert(node);
+  TNode parent = node.parent();
+  if (!parent)
+    ; // nothing to do, the cursor is not in the document any more
+  else if (parent.isG())
+    {
+      TNode next = node.next();
+      if (next) next.insert(cursor);
+      else parent.append(cursor);
+    }
+  else if (parent.isC())
+    {
+      if (node.next())
+       ; // cursor removed
+      else
+       {
+         Frame& frame = frames.top();
+         if (frame.pos == frame.entry.pattern.size())
+           {
+             frames.pop();
+             advance(parent);
+           }
+         else if (frame.entry.paramDelimited(frame.pos))
+           {
+             // the next argument is delimited, so we have to create a phantom group
+             // with the cursor inside. We have to remember that, since we are here, 
+             // the cursor has been removed
+             TNode g = doc.createG();
+             g.append(cursor);
+             parent.append(g);
+           }
+         else
+           {
+             // otherwise, the next MACRO's argument is not delimited, so we just 
+             // append the cursor to the MACRO
+             parent.append(cursor);
+           }
+       }
+    }
+  else if (parent.is("math"))
+    ; // we are done
+  else
+    advance(parent);
+}
+*/
+
+/*
+ * original advance
 void
 TPushParser::advance(const TNode& node)
 {
@@ -659,10 +1684,53 @@ TPushParser::advance(const TNode& node)
   else
     advance(parent);
 }
+*/
+
 
 void
-TPushParser::setCursor(const std::string& c)
+TPushParser::setCursorHint(const std::string& c)
 {
-  cursor["val"] = c;
-  if (listener) listener->callback(doc);
+  if (cursor["val"] != c)
+    {
+      cursor["val"] = c;
+      if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+    }
+}
+
+bool
+TPushParser::hideCursor()
+{
+  if (hiddenCursor++ == 0)
+    {
+      cursor["visible"] = "0";
+      if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+      return true;
+    }
+  else
+    return false;
+}
+
+bool
+TPushParser::showCursor()
+{
+  if (hiddenCursor > 0 && --hiddenCursor == 0)
+    {
+      cursor["visible"] = "1";
+      if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
+      return true;
+    }
+  else
+    return false;
+}
+
+bool
+TPushParser::thaw()
+{
+  if (APushParser::thaw() && factory && doc.dirtyNode())
+    {
+      factory->documentModified(doc);
+      return true;
+    }
+  else
+    return false;
 }