]> matita.cs.unibo.it Git - helm.git/commitdiff
Added some controls for the graphical deleting of apostrophe.
authorPaolo Marinelli <paolo.marinelli@unibo.it>
Tue, 4 Feb 2003 16:21:10 +0000 (16:21 +0000)
committerPaolo Marinelli <paolo.marinelli@unibo.it>
Tue, 4 Feb 2003 16:21:10 +0000 (16:21 +0000)
The code for the graphical deleting is now divided in several private methods, which
 make the code clearer.
Partially added the use of the logger in the class TPushParser.
Some little errors has been eliminated in the implementation of the class
TPushParser.

helm/DEVEL/mathml_editor/src/TPushParser.cc
helm/DEVEL/mathml_editor/src/TPushParser.hh

index 325b9aa3c129f9a9bd31500aeec01ed6850184f1..870e9f0a707a6b86d05c24ef9b7a4677729d3b40 100644 (file)
@@ -21,6 +21,7 @@ TPushParser::init()
   cursor["id"] = "I0";
   doc.clearDirty();
   doc.root().append(cursor);
+  logger.verbosity(ALogger::Debug);
 }
 
 TPushParser::~TPushParser()
@@ -75,22 +76,23 @@ TPushParser::do_end()
       assert(row && row.is("row"));
       TNode table = row.parent();
       assert(table);
-      advance(table);
-
-      // now we have to pop the associated frame in the stack
-      frames.pop();
+      advance(row);
     }
   else if (parent && parent.isG() && !parent.hasId() && parent.parent())
     {
       // 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());
-      frames.pop();
+      advance(parent);
     }
   else
     {
-      // ???
-      assert(0);
+      // In this case, there is a redundant '}', so I think we can ignore it and 
+      // emit an error
+      logger.error("There so no corresponding'{'");
+      //assert(0);
     }
 }
 
@@ -121,6 +123,7 @@ TPushParser::do_shift()
          else
            {
              // we need two closing math shifts
+             //cursor.remove(); ??
              parent.parent().append(cursor);
            }
        }
@@ -141,7 +144,13 @@ TPushParser::do_shift()
     }
   else
     {
-      cerr << "ERROR: math shift" << endl;
+      // In TeX, the user can type
+      //   $a-b\over2a+b$
+      // In this case, '$' character is correct.
+      // I don't think so, if user types
+      //   $12+{a-b\over2a+b$
+      // because the group is not closed with a '}'
+      logger.error("ERROR: math shift");
     }
 }
 
@@ -175,7 +184,7 @@ TPushParser::do_align()
     }
   else
     {
-      cerr << "alignment tab used outside matrix" << endl;
+      logger.error("alignment tab used outside matrix");
     }
 }
 
@@ -216,7 +225,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";
     }
 }
@@ -246,7 +255,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";
     }
 }
@@ -316,7 +325,8 @@ TPushParser::do_apostrophe()
        }
       else
        {
-         // error ???
+         // is it an error?
+         logger.error("you have to type one identifier before the  ''");
        }
     }
   else
@@ -417,9 +427,9 @@ TPushParser::do_control(const std::string& name)
            TNode m = doc.createC(name, nextId++);
            cursor.replace(m);
            
-           cout << "ecco tutti i token del pattern della entry inserita" << endl;
+           logger.debug("do_control: i have pushed a frame in the stack. Here is the pattern of the associated MACRO");
            for (unsigned i = 0; i < entry.pattern.size(); i++)
-             cout << entry.pattern[i].value << endl;
+             logger.debug(entry.pattern[i].value);
            
            if (entry.leftOpen && entry.rightOpen)
              {
@@ -458,12 +468,8 @@ TPushParser::do_control(const std::string& name)
                  {
                    frames.push(Frame(entry));
 
-                   if (entry.table)
-                     cout << "do_control: we have a table" << endl;
-                   
                    if (entry.paramDelimited(0))
                      {
-                       cout << "do_control: it'a MACRO with delimited first argument" << endl;
                        TNode g = doc.createG();
                        m.append(g);
                        g.append(cursor);
@@ -474,7 +480,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("error, but we could handle this easily");
                  }
              }
            else advance(m);
@@ -482,7 +488,7 @@ TPushParser::do_control(const std::string& name)
          break;
        case TDictionary::UNDEFINED:
          {
-           cerr << "ERROR: using undefined macro `" << name << "'" << endl;
+           logger.error("ERROR: using undefined macro ` + name '");
            TNode m = doc.createC(name, nextId++);
            cursor.replace(m);
            advance(m);
@@ -495,145 +501,278 @@ TPushParser::do_control(const std::string& name)
 }
 
 void
-TPushParser::gdelete_prev()
+TPushParser::gdelete_prev_token()
 {
-  // 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"))
+  assert(prev.is("i") || prev.is("o") || prev.is("n"));
+
+  logger.debug("gdelete_prev_token: start");
+  
+  // 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 ((ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
     {
-      // 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 ((ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
+      logger.debug("gdelete_prev_token: i have removed an element");
+      cursor.remove();
+      prev.replace(cursor);
+      
+      if (cursor.parent().isC())
         {
-         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);
+         // 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));
+         frame.pos--;
+         logger.debug("gdelete_prev_token: it was a MACRO's child so i have decremented the member pos of the associated frame");
        }
-    } // end of if (prev.is("i") || ...)
-  else if (prev.is("sp") || prev.is("sb"))
+    }
+  else
     {
-      cursor.remove();
-      prev.append(cursor);
-      gdelete_prev();
-    } // end of if (prev.is("sp") || prev.is("sb"))
-  else if (prev.isG())
+      ucs4val.erase(ucs4val.length() - 1, 1);
+      prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
+    }
+
+  logger.debug("gdelete_prev_token: bye...");
+}
+
+void
+TPushParser::gdelete_prev_script()
+{
+  logger.debug("gdelete_prev_script: start...");
+  // this method delete a 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);
+  // i can invoke the gdelet_prev, because a sp (sb) MUST have two child
+  gdelete_prev();
+  logger.debug("gdelete_prev_script: bye...");
+}
+
+void
+TPushParser::gdelete_prev_group()
+{
+  logger.debug("gdelete_prev_group: start");
+  assert(cursor.prev() && cursor.prev().isG());
+  TNode prev = cursor.prev();
+  cursor.remove();
+  prev.append(cursor);
+  logger.debug("gdelete_prev_group: i have to call the do_gdelete()");
+  
+  // a group can have no child, so the gdelete_prev is not appropriate
+  // so this method is not equivalent to the one above
+  do_gdelete();
+  
+  logger.debug("gdelete_prev_group: do_gdelete terminated");
+  logger.debug("gdelete_prev_group: bye...");
+}
+
+void
+TPushParser::gdelete_prev_macro()
+{
+  logger.debug("gdelete_prev_macro: start");
+  assert(cursor.parent());
+  assert(cursor.prev());
+  TNode prev = cursor.prev();
+  assert(prev.isC());
+  
+  const TDictionary::Entry& entry = dictionary.find(prev["name"]);
+  
+  if (!entry.defined())
     {
+      // I assume tha the user want to completely delete the undefined macro
+      logger.debug("gdelete_prev_macro: i have to remove an undefined macro");
       cursor.remove();
-      prev.append(cursor);
-      do_gdelete();
+      prev.replace(cursor);
     }
-  else if (prev.isC() && dictionary.find(prev["name"]).cls != TDictionary::UNDEFINED)
+  else
     {
-      const TDictionary::Entry& entry = dictionary.find(prev["name"]);
-
-      // i start to remove a MACRO. The frame associated to this MACRO was 
-      // popped from the stack (i think). So, i re-push the frame in the stack,
-      // but the pos member should be set correctly
-      cout << "gdelete_prev: i have to start to delete a MACRO" << endl;
+      logger.debug("gdelete_prev_macro: i have to start to delete a defined MACRO");
+      
+      // i start to remove a MACRO. Different actions must be taken, based on the nature 
+      // of the MACRO. In some case, we can't remove the MACRO immediately, in other
+      // case it's correct. In the first case, we have to update the stack, pushing
+      // a frame in the stack with a correct value of pos, in the 
+      // second one, we do not have to push a frame in the stack
       
-      // if the control element is leftOpen and rightOpen, the cursor should be placed after 
-      // the last child of the control element's last child
       if (entry.rightOpen)
         {
-         cout << "gdelte_prev(): i have to delete a control rightOpen, so i push an element in the stack" << endl;
-         Frame frame(entry);
-         frames.push(frame);
+         // if the control element is ightOpen, the cursor should be placed after 
+         // the last child of the control element's last child, and try to remove something
+         // A frame MUST be pushed in the stack, because we dont' know if the following actions 
+         // will remove the MACRO.
+         logger.debug("gdelte_prev_macro: i have to delete a control rightOpen, so i push an element in the stack");
+         
+         frames.push(Frame(entry));
+
+         logger.debug("gdelete_prev_macro: i control the values of the pushed frame");
+         logger.debug("gdelete_prev_macro: rightOpen");
+         cout << frames.top().entry.rightOpen << endl;
+         logger.debug("gdelete_prev_macro: leftOpen");
+         cout << frames.top().entry.leftOpen << endl;
+         logger.debug("gdelete_prev_macro: pattern.empty()");
+         cout << frames.top().entry.pattern.empty() << endl;
+
+         // 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);
-         do_gdelete();
+         
+         // the gdelete_prev is not appropriate, because the last child of the MACRO could have no child
+         logger.debug("gdelete_prev_macro: i have to call do_gdelete_phantom_group");
+         do_gdelete_phantom_group();
+         
+         if (!frames.empty())
+           {
+             logger.debug("gdelete_prev_macro: after the do_gdelete_phantom_group, the frame in the stack has to be rightOpen");
+             cout << frames.top().entry.rightOpen  << endl;
+             logger.debug("gdelte_prev_macro: after the do_gdelete_phantom_group, the top element's pattern MUST be empty");
+             cout << frames.top().entry.pattern.empty() << endl;
+           }
+         else
+           logger.debug("gdelete_prev_macro: the do_gdelete_phantom_group has removed all frames in the stack");
+         
        }
       else if (entry.leftOpen)
         {
-         cout << "gdelete_prev: i have to delete a control element with leftOpen" << endl;
+         logger.debug("gdelete_prev_macro: i have to delete a control element with 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, i don't remove the last child of the phantom group, but
+         // i don't know if it's the correct behavior of the graphical deleting.
+         // To delete it, just remove the comment of the last instruction 
          assert(prev.first());
          assert(prev.first().isG());
          assert(prev.first() == prev.last());
+         
          TNode g = prev.first();
          g.remove();
          prev.replace(g.first(), TNode());
+         //do_gdelete();
        }
       else if (!entry.pattern.empty())
-        { 
+        {
          // we have to start removing a MACRO which accepts arguments.
          // a MACRO without child does not exist
          
-         cout << "gdelete_prev: i have to remove a MACRO with argument" << endl;
-
+         logger.debug("gdelete_prev_macro: i have to remove a MACRO with argument");
          assert(prev.size() >= 1);
 
+         // Differnt actions should 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())
            {
-             // this means that the last child of the MACRO is a phantom group,
-             // which in turn means that it is a delimited argument
-             // so we have to ideally remove this delimiter
+             // the last argument of the MACRO is a delimited argumet. We ideally remove 
+             // the sequence of delimiters
+             logger.debug("gdelete_prev_macro: the last argument of the MACRO is delimited");
              cursor.remove();
              prev.last().append(cursor);
              // i have to push a frame with a correct value of pos
-             Frame frame(entry);
              assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
              unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
-             frame.pos = entry.pattern.size() - sequence_length - 1;
-             frames.push(frame);
-             cout << "gdelete_prev: i have inserted a frame, it's pos is: " << frames.top().pos << endl;
+             unsigned p = entry.pattern.size() - sequence_length - 1;
+             frames.push(Frame(entry, p));
+             logger.debug("gdelete_prev_macro: i have inserted a frame, it's pos is: ");
+             cout << frames.top().pos << endl;
+             
            }
          else
            {
-             // in this case, the last child of the MACRO is 
-             // an argument which is NOT delimited, so i try to 
-             // remove it
-             cout << "gdelete_prev: i try to remove the last argumet of the MACRO" << endl;
+             // 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. 
+             logger.debug("gdelete_prev_macro: the last argumet of the MACRO is not delimited, so itry to remove it");
              cursor.remove();
              if (entry.table == 1 && prev.last().is("row"))
                {
-                 // in this case should be appended to the group associated to 
+                 // in this case the cursor should be appended to the group associated to 
                  // the last cell of the last row of the table
+                 logger.debug("gdelete_prev_macro: but it is a table with rows, so i append the cursor at the end of the table");
                  assert(prev.last().last().is("cell") && prev.last().last().first().isG());
                  prev.last().last().first().append(cursor);
                }
-             else prev.append(cursor);
+             else
+               {
+                 logger.debug("gdelete_prev_macro: i append the cursor to the MACRO");
+                 prev.append(cursor);
+               }
+
+             frames.push(Frame(entry, entry.pattern.size()));
 
-             Frame frame(entry);
-             frame.pos = entry.pattern.size();
-             frames.push(frame);
+             logger.debug("gdelete_prev_macro: i've pushed a frame in the stack, and it's value of pos is ");
+             cout << frames.top().pos << endl;
              
-             if (cursor.prev()) gdelete_prev();
-             else do_gdelete();
+             if (cursor.prev())
+               {
+                 logger.debug("gdelete_prev_macro: i invoke the gdelete_prev");
+                 gdelete_prev();
+               }
+             else
+               {
+                 logger.debug("gdelete_prev_macro: i invoke the do_gdelete");
+                 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 not rightOpen, nor leftOpen,
+         // and has no pattern. It means that it has no childs.
+         // We can replace it with the cursor
+         logger.debug("gdelete_prev_macro: the MACRO is empty, i remove it");
+         assert(prev.size() == 0);
+         cursor.remove();
+         prev.replace(cursor);
        }
+    } // end of defined MACRO
+
+  logger.debug("gdelete_prev_macro: bye...");
+}
+
+void
+TPushParser::gdelete_prev()
+{
+  // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
+  
+  logger.debug("gdelete_prev: start...");
+
+  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 (dictionary.find(prev["name"]).cls == TDictionary::UNDEFINED)
+  else if (prev.isSp() || prev.isSb())
     {
-      // The user wants to delete an undefined element.
-      // I suppose that he (or she) wants to delete it all
-      cursor.remove();
-      prev.replace(cursor);
+      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
+      // not handled. Future cases...
     }
-  
+
+  logger.debug("gdelete_prev: bye...");
 } // end of method
 
 void
@@ -667,6 +806,495 @@ TPushParser::rgreplace_futher(void)
     }
 }
 
+void
+TPushParser::do_gdelete_script()
+{
+  // If we are here, the cursor is child of a script and 
+  // means that a prev MUST exist and that there is only an 
+  // 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.
+
+  logger.debug("do_gdelete_script: start...");
+
+  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 i think 
+      // we have to remove the entire MACRO, replacing it with the cursor
+      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 it. This controls are made
+      // in the method rgreplace_futher().
+      if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_futher();
+    }
+  else
+    {
+      // in this case, the prev has to replace the script, 
+      // and the curosor has to be placed after the prev. 
+      assert(prev.hasId());
+      parent.replace(prev);
+      prev.parent().append(cursor);
+      // now prev should have a preceding element
+      assert(cursor.parent().size() > 1);
+    }
+  
+ logger.debug("do_gdelete_script: bye...");
+  
+} // end of method do_gdelet_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 was waiting for a non 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
+  logger.debug("do_gdelete_macro:");
+  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)
+      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 could remove it...but i'm not sure
+      if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
+       rgreplace_futher();
+      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 could be false.
+         assert(!frames.empty());
+         Frame& frame = frames.top();
+         assert(frame.pos > 0);
+         frame.pos--;
+       }
+    }
+  else
+    {
+      // we have to control if prev is 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);
+         logger.debug("do_gdelete_macro: the pos of the frame is: ");
+         cout << frame.pos << endl;
+         logger.debug("do_gdelete_macro: the pattern size of the entry is");
+         cout << frame.entry.pattern.size() << endl;
+         logger.debug("do_gdelete_macro: the prev param is at");
+         cout << frame.entry.previousParam(frame.pos) << endl;
+         cursor.remove();
+         prev.append(cursor);
+         assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
+         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. 
+         // I told "try" because the prev can be a group or something that 
+         // a simple delete cannot remove completely
+         gdelete_prev();
+       }
+    }
+
+  logger.debug("do_gdelete_macro: bye...");
+}
+
+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 remoev it
+      gdelete_prev();
+
+      // We control if the group has to be removed, because the cursor 
+      // could be the only element of the group.
+      if ((parent.first() == cursor) && parent.isG() && parent.hasId())
+       rgreplace_futher();
+      
+    }
+  else
+    {
+      // the cursor has no preceding elements, so we have to remove the 
+      // group.
+      rgreplace_futher();
+      
+      // i have to re-start the process, because it' a graphical delete
+      do_gdelete();
+    }
+
+  logger.debug("do_gdelete_groupId: bye...");
+  
+} // 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.
+  logger.debug("do_gdelete_phantom_group: start");
+  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
+      logger.debug("do_gdelete_phantom_group: i have to call the gdelete_prev()");
+      gdelete_prev();
+      logger.debug("do_gdelete_phantom_group: gdelete_prev terminated");
+
+      if (parent.size() == 1 && parent.parent().isSp())
+        {
+         logger.debug("do_gdelete_phantom_group: i have to remove a phantom group which is a child of a sp");
+         // in this case the gdelete_prev has removed the only element preceding the cursor.
+         // If the phantom group is a sp's child, it means that the user has removed all \' in the 
+         // phantom group. I think, we can remove the phamtom group and the sp element.
+         cursor.remove();
+         parent.replace(cursor);
+         // now we have a sp element with two children: the first child (we don't know nothing 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
+         do_gdelete_script();
+       }
+      else if (parent.parent().isSp())
+        {
+         // in this case we have to place the cursor after the sp element
+         logger.debug("do_gdelete_phantom_group: the sequence of \' is not terminated, so i 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 futher of the cursor.
+      TNode gfuther = parent.parent();
+      if (!gfuther)
+        {
+         // If here, the TML tree is in an inconsistent state
+         logger.error("do_gdelete_phantom: TML tree in a inconsistent state");
+       }
+      else if (gfuther.isC())
+        {
+         // in this case the phantom group is child of a MACRO.
+         // We have to control the nature of this MACRO.
+         logger.debug("do_gdelete_phantom_group: i have to remove a phantom group which is a child of a MACRO");
+         assert(!frames.empty());
+          Frame& frame = frames.top();
+         
+         if (frame.entry.leftOpen && frame.entry.rightOpen)
+           {
+             // in this case, the cursor is in the second and last child 
+             // of the MACRO. We can assert that the grand futher has two 
+             // children. which are both phantom group
+             logger.debug("do_gdelete_phantom_group: the MACRO is leftOpen and rigthOpen");
+             assert(gfuther.size() == 2);
+             assert((gfuther.last() == parent) && (gfuther.first().isG() && !gfuther.first().hasId()));
+             assert(frame.pos == 0);
+             
+             TNode ggfuther = gfuther.parent();
+             assert(ggfuther);
+             cursor.remove();
+             parent.remove();
+             // i have to replace the gfuther with the elements of its first child
+             gfuther.replace(gfuther.first().first(), TNode());
+             logger.debug("do_gdelete_phantom_group: i have removed the control element, and replaced it with its first child");
+             ggfuther.append(cursor);
+             logger.debug("do_gdelete_phantom_group: cursor appended to the grand grand futher");
+             // now we have the situation preceding the insertion of the MACRO leftOpen and rightOpen
+             // this MACRO no longer exists.
+             frames.pop();
+           }
+         else if (frame.entry.rightOpen)
+           {
+             // the user has inserted a MACRO rightOpen. Since the cursor is the 
+             // only child of the MACRO, the user want to remove it. 
+             // We can assert that cursor's parent is the only child of the MACRO
+             logger.debug("do_gdelete_phantom_group: the MACRO is rightOpen only");
+             assert(gfuther.size() == 1);
+             assert(frame.pos == 0);
+             cursor.remove();
+             parent.remove();
+             gfuther.replace(cursor);
+             
+             // now we have the situation preceding the MACRO rightOpen, so i have to pop the frame
+             frames.pop();
+           }
+         else if (frame.entry.leftOpen)
+           {
+             // i think this situation will never occur
+             // but it can be recovered in some way
+             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.
+             logger.debug("do_gdelete_phantom_group: the MACRO accepts arguments");
+             logger.debug("do_gdelete_phantom_group: entry pattern has size:");
+             cout << frame.entry.pattern.size() << endl;
+             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();
+                 gfuther.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 sequrnce 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 i 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("do_gdelete_phantom_group: TML tree in a strange state");
+             if (gfuther.size() == 1)
+               {
+                 cursor.remove();
+                 parent.remove();
+                 gfuther.replace(cursor);
+               }
+             else
+               {
+                 logger.debug("do_gdelete_phantom_group: TML tree in a very strange state");
+                 cursor.remove();
+                 gfuther.replace(cursor);
+               }
+           }
+       } // end of if (gfuther.isC())
+      else if (gfuther.is("cell"))
+        {
+         // we have to handle the case where cursor'grand futher is a cell element.
+         // The tables are control sequece, 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 child of row element, which in turn MUST be child of an element 
+         // havin attribute table.
+         assert(gfuther.parent() && gfuther.parent().is("row") && gfuther.parent().parent());
+         TNode row = gfuther.parent();
+         
+         // in this case the cell has no element, so the user wants to delete this cell.
+         TNode prev_cell = gfuther.prev();
+         cursor.remove();
+         parent.remove();
+         gfuther.remove();
+         // now the cell no longer exists
+
+         if (!prev_cell)
+           {
+             // i this case, the cell is the only cell in the row.
+             // So, i 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. 
+                 // I think 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 at least)
+                 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 (gfuther.is("cell"))
+      else if (gfuther.isSp())
+        {
+         // in this case, the user pushed 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 choice: 
+         //   delete the phantom group;
+         //   delete the superscript.
+       }
+      else
+        {
+         // cursor' grand futher is undefined
+         logger.error("do_gdelete_phantom_group: TML tree in a unknown state");
+       }
+    } // end of the else of the if (prev)
+
+  if (!frames.empty())
+    {
+      logger.debug("do_gdelete_phantom_group: the stack is not empty");
+      logger.debug("do_gdelte_phanto_group: is the top element rightOpen?");
+      cout << frames.top().entry.rightOpen << endl;
+      logger.debug("do_gdelte_phanto_group: is the top element'pattern empty?");
+      cout << frames.top().entry.pattern.empty()  << endl;
+    }
+  logger.debug("do_gdelete_phantom_group: bye...");
+}
+
+
+void
+TPushParser::do_gdelete()
+{
+  logger.debug("do_gdelete: start");
+  // 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...remove the math mode (it implies controlling the display attribute), do nothing
+         // At the moment, the user's will of deleting is simply ignored
+         logger.error("TML tree not well structured");
+       }
+      else if (parent.isG())
+        {
+         // the cursor's parent is a group. We have to control if it's a phantom group or not
+         logger.debug("do_gdelete: the cursor's parent is a group");
+         if (parent.hasId())
+           {
+             logger.debug("do_gdelete: which has Id");
+             do_gdelete_groupId();
+           }
+         else
+           {
+             logger.debug("do_gdelete: which is a phantom group");
+             do_gdelete_phantom_group();
+           }
+       } // end of parent is group
+      else if (parent.isC())
+        {
+         cout << "the cursor's parent is a MACRO" << endl;
+         do_gdelete_macro();
+       } // end of parent is a MACRO
+      else if (parent.isSp() || parent.isSb())
+        {
+         cout << "the cursor's parent is a script" << endl;
+         do_gdelete_script();
+       } // end of parent is sp or sb
+    } // end of the else which consider the case in which parent exists
+  
+  if (!cursor.parent())
+    logger.debug("do_gdelete: the cursro has no parent -> paolo is stupid");
+
+  if (!frames.empty())
+    {
+      logger.debug("do_gdelete: the stack is not empty");
+      logger.debug("do_gdelete: is the top element rightOpen?");
+      cout << frames.top().entry.rightOpen << endl;
+      logger.debug("do_gdelete: is the top element'pattern empty");
+      cout << frames.top().entry.pattern.empty() << endl;
+    }
+  logger.debug("do_gdelete: bye...");
+  
+} // end of method do_gdelete
+
+/*
 void
 TPushParser::do_gdelete()
 {
@@ -722,8 +1350,9 @@ TPushParser::do_gdelete()
 
                          // being here means that there is a frame in the stack
                          assert(!frames.empty());
-                         cout << "do_gdelete: i have to remove a phantom group whuch is a child of a MACRO" << endl;
+                         cout << "do_gdelete: i have to remove a phantom group which is a child of a MACRO" << endl;
                          Frame& frame = frames.top();
+                         cout << frame.entry.leftOpen << frame.entry.rightOpen << endl;
                          if (frame.entry.leftOpen && frame.entry.rightOpen)
                            {
                              // in this case, the cursor is in the second and last child 
@@ -778,6 +1407,7 @@ TPushParser::do_gdelete()
                                {
                                  // in this case, we can assert that frame in the stack has 
                                  // pos greater than 0
+                                 cout << "this is the assert that fails" << endl;
                                  assert(frame.pos > 0);
                                
                                  // cursor's uncle does exist. we have to control 
@@ -970,7 +1600,7 @@ TPushParser::do_gdelete()
          
          TNode prev = cursor.prev();
          cursor.remove();
-         if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
+         if (prev.isG()*/ /*&& !prev.hasId()*/ /* && (prev.size() == 0))
            {
              prev.remove();
              parent.replace(cursor);
@@ -1002,6 +1632,7 @@ TPushParser::do_gdelete()
     }
 
 } // end of method
+*/
 
 void
 TPushParser::process(const TToken& token)
@@ -1030,16 +1661,18 @@ TPushParser::process(const TToken& token)
 void
 TPushParser::push(const TToken& token)
 {
-  cerr << "TPushParser::push " << token.value << " (cat: " << token.category << ")" << endl;
+  logger.debug("TPushParser::push " + token.value + " (cat: ");
+  cout << token.category << ")" << endl;
 
   if (token.category == TToken::GDELETE)
     {
-      cout << "push: i have to process a token with category member = GDELETE" << endl;
+      logger.debug("push: i have to process a token with category member = GDELETE");
       process(token);
     }
-  else if ((doc.root().first() && doc.root().first().is("math")) || token.category == TToken::SHIFT)
+  else
     {
 
+      logger.debug("push: i get the cursor's parent");
       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
@@ -1052,13 +1685,19 @@ TPushParser::push(const TToken& token)
         if (parent.isG() && !parent.hasId() && parent.parent().isC())
           {
            // There must be an open frame, for the grand-parent is a control sequence
+           logger.debug("push: cursor'parent is a phantom group, and this group has a MACRO as parent");
+           logger.debug("push: i assert that !frames.empty()");
            assert(!frames.empty());
+           logger.debug("push: this assert is OK");
+           logger.debug("push: now i take the top element of the stack");
            Frame& frame = frames.top();
+           logger.debug("push: now i've got the top element");
            if (!frame.entry.pattern.empty())
              {
                // The entry pattern is not empty. By our conventions this means
                // the entry cannot be open at either end, hence we are parsing
                // a delimited argument
+               logger.debug("push: the frame.entry accepts arguments");
                assert(frame.pos + 1 < frame.entry.pattern.size());
                assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
                if (frame.entry.pattern[frame.pos + 1] == token)
@@ -1122,8 +1761,11 @@ TPushParser::push(const TToken& token)
                // entry. What happens if we actually are in the left side?
                // This could happen only when re-editing an entered expression
                // We'll see...
+               logger.debug("push: the frame.entry has no arguments, so i assert that i's rightOpne");
                assert(frame.entry.rightOpen);
+               logger.debug("push: this assert is OK, now i process the token");
                process(token);
+               logger.debug("push: the token has been processed");
              }
           }
         else if (parent.isC())
@@ -1145,6 +1787,7 @@ TPushParser::push(const TToken& token)
                    // it will be important for the parser to know that the entry
                    // has been completed in order to place the cursor correctly
                    // in the next position
+                   logger.debug("TPushParser.push: we should be here");
                    frame.pos++;
                    process(token);
                  }
@@ -1178,21 +1821,20 @@ TPushParser::push(const TToken& token)
         {
           cout << "ignored token" << endl;
         }
-      
-      //if (listener) listener->callback(doc); //it shoul be repristened if you remove the comment in the else above
 
-    } // this end corresponds to the if ((doc.root().first() && doc.root().first().is("math")) || token.category == TToken::SHIFT)
-  else
-    {
-      // there is no math element
-      // In this case, i think we have to emit an error
-      cout << "push: ignored token...you have to enter in math mode...insert $" << endl;
-    }
+    } // this end corresponds to the else of the if (token.category == TToken::GDELETE)
 
   if (factory) factory->documentModified(doc);
 
   if (frames.empty()) cout << "stack vuoto" << endl;
-  else cout << "stack non vuoto" << endl;
+  else
+    {
+      logger.debug("push: the stack is not empty");
+      logger.debug("push: is the top element rightOpen?");
+      cout << frames.top().entry.rightOpen << endl;
+      logger.debug("push: is the pattern empty");
+      cout << frames.top().entry.pattern.empty() << endl;
+    }
 }
 
 void
index 09bfe713db9a94321fa1f3873bc4d67fccff8f32..143e8fad13acc2646228726269a7ac2d2e399720 100644 (file)
@@ -44,8 +44,20 @@ private:
   void do_active(const std::string&);
   void do_comment(void);
   void do_control(const std::string&);
+
+  
+  void gdelete_prev_token(void);
+  void gdelete_prev_script(void);
+  void gdelete_prev_group(void);
+  void gdelete_prev_macro(void);
   void gdelete_prev(void);
+  
   void rgreplace_futher(void);
+
+  void do_gdelete_script(void);
+  void do_gdelete_macro(void);
+  void do_gdelete_groupId(void);
+  void do_gdelete_phantom_group(void);
   void do_gdelete(void);
 
   void do_cr(void);
@@ -57,6 +69,7 @@ private:
   struct Frame
   {
     Frame(const TDictionary::Entry& e) : entry(e), pos(0) { };
+    Frame(const TDictionary::Entry&e, unsigned p) : entry(e), pos(p) { };
     const TDictionary::Entry& entry;
     unsigned pos;
   };