]> matita.cs.unibo.it Git - helm.git/blob - helm/DEVEL/mathml_editor/src/TPushParser.cc
New version 0.0.2:
[helm.git] / helm / DEVEL / mathml_editor / src / TPushParser.cc
1 /* This file is part of EdiTeX, an editor of mathematical
2  * expressions based on TeX syntax.
3  * 
4  * Copyright (C) 2002-2003 Luca Padovani <lpadovan@cs.unibo.it>,
5  *                    2003 Paolo Marinelli <pmarinel@cs.unibo.it>.
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * For more information, please visit the project's home page
22  * http://helm.cs.unibo.it/editex/
23  * or send an email to <lpadovan@cs.unibo.it>
24  */
25
26 #include "ALogger.hh"
27 #include "TPushParser.hh"
28 #include "AMathMLFactory.hh"
29
30 TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d)
31 {
32   init();
33 }
34
35 TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d)
36 {
37   init();
38 }
39
40 TPushParser::~TPushParser()
41 {
42 }
43
44 void
45 TPushParser::init()
46 {
47   cursor = doc.create("cursor");
48   cursor["visible"] = "1";
49   hiddenCursor = 0;
50   reset();
51 }
52
53 void
54 TPushParser::reset()
55 {
56   nextId = 1;
57   if (cursor.parent()) cursor.remove();
58   cursor["val"] = "";
59   doc.reset();
60   doc.root().append(cursor);
61   if (factory && !frozen()) factory->documentModified(doc);
62 }
63
64 TNode
65 TPushParser::PRIME()
66 {
67   const TDictionary::Entry entry = dictionary.find("prime");
68   if (entry.cls == TDictionary::OPERATOR)
69     {
70       TNode op = doc.createO(entry.value, nextId++);
71       op["name"] = "prime";
72       return op;
73     }
74   else
75     {
76       TNode op = doc.createO("?", nextId++);
77       return op;
78     }
79 }
80
81 bool
82 TPushParser::do_begin()
83 {
84   TNode parent = cursor.parent();
85   if (parent.isC() && dictionary.find(parent.nameC()).table)
86     {
87       TNode row = doc.create("row");
88       TNode cell = doc.create("cell");
89       TNode g = doc.createG();
90       row.append(cell);
91       cell.append(g);
92       g.append(cursor);
93       parent.append(row);
94     }
95   else
96     {
97       TNode g = doc.createG(nextId++);
98       cursor.replace(g);
99       g.append(cursor);
100     }
101   return true;
102 }
103
104 bool
105 TPushParser::correctBrace()
106 {
107   // this method MUST be invoked when the cursor is child of a 
108   // phantom group, which in turn is the last rightOpen MACRO's child.
109   // The only way to exit from a rightOpen MACRO is opening a group before 
110   // inserting the MACRO and, once the MACRO is completely inserted, closing 
111   // the group.
112   // This method return true if the condition above is true. False, otherwise.
113   assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
114   TNode parent = cursor.parent();
115   assert(parent.parent() && parent.parent().isC());
116   assert(!frames.empty());
117   Frame& frame = frames.top();
118   assert(frame.entry.rightOpen);
119   assert(parent.parent().last() == parent);
120   
121   TNode c = parent.parent();
122   bool stop = false;
123   bool ok = false;
124   TNode node = c.parent();
125   do
126     {
127       if (node.isG() && node.hasId())
128         {
129           // in this case, the rightOpen MACRO is a child of a group with id. 
130           // So, the '}' is correct
131           ok = true;
132           stop = true;
133         }
134       else if (node.isG())
135         {
136           // the MACRO is a phantom group's child. We have to control why we 
137           // have this phantom group
138           TNode nodeParent = node.parent();
139           if (nodeParent && nodeParent.isC())
140             {
141               // we have to control the nature of this MACRO
142               const TDictionary::Entry& entry = dictionary.find(nodeParent.nameC());
143               if (entry.rightOpen && node == nodeParent.last())
144                 {
145                   // in this case we have to re-iterate the process
146                   node = nodeParent.parent();
147                 }
148               else stop = true;
149             }
150           else stop = true;
151         }
152       else
153         {
154           // at the moment we assume that a MACRO cannot be child of an element other than a group
155           stop = true;
156         }
157     }
158   while (!stop);
159
160   return ok;
161 }
162
163 bool
164 TPushParser::do_end()
165 {
166   TNode parent = cursor.parent();
167   if (parent && parent.isG() && parent.hasId())
168     {
169       // normal closing brace for an explicitly open group
170       cursor.remove();
171       advance(parent);
172       return true;
173     }
174   else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
175     {
176       assert(!frames.empty());
177       // closing brace for a structure in which & or \cr have been used
178       TNode row = parent.parent().parent();
179       assert(row && row.is("row"));
180       assert(row.parent());
181       advance(row);
182       return true;
183     }
184   else if (parent && parent.isG() && !parent.hasId() && parent.parent() && !parent.parent().is("math"))
185     {
186       // In this case, we have to control the cursor's grand parent.
187       TNode gparent = parent.parent();
188
189       if (gparent.isC() && gparent.last() == parent)
190         {
191           // a frame MUST be in the stack
192           assert(!frames.empty());
193
194           // we have to control the nature of this macro
195           if (frames.top().entry.rightOpen)
196             {
197               // in this case, the '}' character is the proper way to exit from the phantom group, and 
198               // in particular, this character means that the user wants to exit from the MACRO.
199               // A rightOpen MACRO MUST be descendant of a group with Id. This '}' is the closing brace of this 
200               // group. So, we have to control if this group exists. This groyp could exist, but this MACRO could 
201               // be another MACRO's child, so we have to control this last MACRO recursively. This recurive control 
202               // is done by the correctBrace method.
203               if (!correctBrace())
204                 {
205                   // the '}' is not correct
206                   logger.warning("nothing to close");
207                   return false;
208                 }
209               else
210                 {
211                   cursor.remove();
212                   advance(parent);
213                   return true;
214                 }
215             }
216           else
217             {
218               logger.warning("ignored closing brace");
219               return false;
220             }
221         }
222       else
223         {
224           // at the moment, a phantom group with the cursor inside can be a MACRO's child or a cell's child, and these cases
225           // are handled in other blocks of code.
226           logger.error("do_end: strange TML tree");
227           return false;
228         }
229     }
230   else
231     {
232       // In this case, there is a redundant '}', so we can ignore it and 
233       // emit an error
234       logger.warning("There is so no corresponding'{'");
235       return false;
236       //assert(0);
237     }
238 }
239
240 bool
241 TPushParser::do_shift()
242 {
243   TNode parent = cursor.parent();
244   assert(parent);
245   if (parent.is("tex"))
246     {
247       TNode math = doc.create("math", nextId++);
248       TNode g = doc.createG();
249       cursor.replace(math);
250       math.append(g);
251       g.append(cursor);
252       return true;
253     }
254   else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
255     {
256       if (cursor.prev())
257         {
258           // there is something before the cursor, hence this is the
259           // closing math shift
260           if (parent.parent()["display"] != "1")
261             {
262               // one math shift is enough to close it
263               cursor.remove();
264               return true;
265             }
266           else
267             {
268               // we need two closing math shifts
269               //cursor.remove(); ??
270               parent.parent().append(cursor);
271               return true; // ???
272             }
273         }
274       else if (parent.parent()["display"] != "1")
275         {
276           // there is nothing before the cursor, and the math is not
277           // in display mode, so this must be a double math shift
278           parent.parent()["display"] = "1";
279           return true;
280         }
281       else
282         {
283           parent.parent().append(cursor);
284           return true;
285         }
286     }
287   else if (parent.is("math"))
288     {
289       cursor.remove();
290       return true;
291     }
292   else
293     {
294       logger.warning("not allowed here");
295       return false;
296     }
297 }
298
299 bool
300 TPushParser::do_align()
301 {
302   TNode parent = cursor.parent();
303   if (parent && parent.isG() && parent.hasId())
304     {
305       // alignment tab used for the first time inside a group
306       TNode row = doc.create("row");
307       TNode cell = doc.create("cell");
308       TNode g = doc.createG();
309       row.append(cell);
310       cell.append(g);
311       g.append(parent.first(), cursor);
312       return true;
313     }
314   else if (parent && parent.isG() && parent.parent().is("cell"))
315     {
316       // alignment tab used within a cell
317       TNode oldCell = parent.parent();
318       assert(oldCell && oldCell.is("cell"));
319       TNode row = oldCell.parent();
320       assert(row && row.is("row"));
321       TNode cell = doc.create("cell");
322       if (oldCell.next()) oldCell.next().insert(cell);
323       else row.append(cell);
324       TNode g = doc.createG();
325       cell.append(g);
326       g.append(cursor);
327       return true;
328     }
329   else
330     {
331       logger.warning("alignment tab used outside matrix");
332       return false;
333     }
334 }
335
336 bool
337 TPushParser::do_eol()
338 {
339   //if (cursor.parent()) cursor.remove();
340   logger.warning("ignored token");
341   return false;
342 }
343
344 bool
345 TPushParser::do_parameter(const std::string& p)
346 {
347   logger.warning("ignored token");
348   return false;
349 }
350
351 bool
352 TPushParser::do_subscript()
353 {
354   TNode parent = cursor.parent();
355   if (parent.isG())
356     {
357       TNode prev = cursor.prev();
358       if (!prev)
359         {
360           TNode elem = doc.create("sb", nextId++);
361           TNode g = doc.createG();
362           cursor.replace(elem);
363           elem.append(g);
364           elem.append(cursor);
365           return true;
366         }
367       else
368         {
369           TNode elem = doc.create("sb", nextId++);
370           prev.replace(elem);
371           elem.append(prev);
372           elem.append(cursor);
373           return true;
374         }
375     }
376   else if (parent.isSb() && cursor == parent[1])
377     {
378       if (parent["under"] == "1")
379         {
380           logger.warning("already under");
381           return false;
382         }
383       else
384         {
385           parent["under"] = "1";
386           return true;
387         }
388     }
389   else
390     {
391       logger.warning("ignored token");
392       return false;
393     }
394 }
395
396 bool
397 TPushParser::do_superscript()
398 {
399   TNode parent = cursor.parent();
400   if (parent.isG())
401     {
402       TNode prev = cursor.prev();
403       if (!prev)
404         {
405           TNode elem = doc.create("sp", nextId++);
406           TNode g = doc.createG();
407           cursor.replace(elem);
408           elem.append(g);
409           elem.append(cursor);
410           return true;
411         }
412       else
413         {
414           TNode elem = doc.create("sp", nextId++);
415           prev.replace(elem);
416           elem.append(prev);
417           elem.append(cursor);
418           return true;
419         }
420     }
421   else if (parent.isSp() && cursor == parent[1])
422     {
423       if (parent["over"] == "1")
424         {
425           logger.warning("already over");
426           return false;
427         }
428       else
429         {
430           parent["over"] = "1";
431           return true;
432         }
433     }
434   else
435     {
436       logger.warning("ignored token");
437       return false;
438     }
439 }
440
441 bool
442 TPushParser::do_ignorablespace(const std::string& s)
443 {
444   // At the moment, do nothing
445 }
446
447 bool
448 TPushParser::do_space(const std::string&)
449 {
450   TNode elem = doc.createS(nextId++);
451   cursor.replace(elem);
452   advance(elem);
453   return true;
454 }
455
456 bool
457 TPushParser::do_letter(const std::string& s)
458 {
459   //TNode parent = cursor.parent();
460   TNode elem = doc.createI(s, nextId++);
461   cursor.replace(elem);
462   advance(elem);
463   return true;
464 }
465
466 bool
467 TPushParser::do_digit(const std::string& s)
468 {
469   TNode elem = doc.createN(s, nextId++);
470   cursor.replace(elem);
471   advance(elem);
472   return true;
473 }
474
475 bool
476 TPushParser::isPrimes(const TNode& node) const
477 {
478   assert(node);
479   return node.isG() && node.last() && node.last().is("o") && node.last()["name"] == "prime";
480 }
481
482 bool
483 TPushParser::do_apostrophe()
484 {
485   if (cursor.parent() && cursor.parent().isG())
486     {
487       if (TNode prev = cursor.prev())
488         {
489           if (prev.isSp() && prev[1] && isPrimes(prev[1]))
490             {
491               prev[1].append(PRIME());
492               return true;
493             }
494           else if (prev.isSb() && prev[0] &&
495                    prev[0].isSp() && prev[0][1] &&
496                    isPrimes(prev[0][1]))
497             {
498               prev[0][1].append(PRIME());
499               return true;
500             }
501           else
502             {
503               TNode elem = doc.create("sp");
504               TNode g = doc.createG();
505               prev.replace(elem);
506               elem.append(prev);
507               elem.append(g);
508               g.append(PRIME());
509               return true;
510             }
511         }
512       else
513         {
514           // is it an error?
515           logger.warning("you have to insert an identifier before a  ''");
516           return false;
517         }
518     }
519   else
520     {
521       logger.warning("cursor has to be in a group");
522       return false;
523     }
524 }
525
526 bool
527 TPushParser::do_other(const std::string& s)
528 {
529   switch (s[0])
530     {
531     case '\'':
532       return do_apostrophe();
533       break;
534     default:
535       /*cout << "TPushParser::do_other " << s << endl;
536       cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;*/
537       TNode elem = doc.createT("o", s, nextId++);
538       cursor.replace(elem);
539       advance(elem);
540       return true;
541       break;
542     }
543 }
544
545 bool
546 TPushParser::do_active(const std::string&)
547 {
548   // ??? space?
549   logger.warning("ignored token");
550   return false;
551 }
552
553 bool
554 TPushParser::do_comment()
555 {
556   // ???
557   return false;
558 }
559
560 bool
561 TPushParser::do_cr()
562 {
563   TNode parent = cursor.parent();
564   if (parent && parent.isG() &&
565       parent.parent() && parent.parent().is("cell") &&
566       parent.parent().parent() && parent.parent().parent().is("row"))
567     {
568       TNode oldRow = parent.parent().parent();
569       assert(oldRow);
570       TNode table = oldRow.parent();
571       assert(table);
572       TNode row = doc.create("row");
573       TNode cell = doc.create("cell");
574       TNode g = doc.createG();
575       if (oldRow.next()) oldRow.next().insert(row);
576       else table.append(row);
577       row.append(cell);
578       cell.append(g);
579       g.append(cursor);
580       return true;
581     }
582   else
583     {
584       // at the moment, \cr can only be used inside a table
585       logger.warning("cr used outside a table");
586       return false;
587     }
588 }
589
590 bool
591 TPushParser::do_control(const std::string& name)
592 {
593   if (name == "cr") return do_cr();
594   else
595     {
596       TNode parent = cursor.parent();
597       const TDictionary::Entry& entry = dictionary.find(name);
598       switch (entry.cls)
599         {
600         case TDictionary::IDENTIFIER:
601           {
602             TNode t = doc.createI(entry.value, nextId++);
603             t["name"] = name;
604             cursor.replace(t);
605             advance(t);
606             return true;
607           }
608           break;
609         case TDictionary::OPERATOR:
610           {
611             TNode t = doc.createO(entry.value, nextId++);
612             t["name"] = name;
613             cursor.replace(t);
614             advance(t);
615             return true;
616           }
617           break;
618         case TDictionary::NUMBER:
619           {
620             TNode t = doc.createN(entry.value, nextId++);
621             t["name"] = name;
622             cursor.replace(t);
623             advance(t);
624             return true;
625           }
626           break;
627         case TDictionary::MACRO:
628           {
629             if (parent.isG())
630               {
631                 TNode m = doc.createC(name, nextId++);
632                 cursor.replace(m);
633                 if (entry.leftOpen && entry.rightOpen)
634                   {
635                     assert(entry.pattern.empty());
636                     assert(parent.isG());
637                     TNode g1 = doc.createG();
638                     g1["left-open"] = "1";
639                     g1.append(parent.first(), m);
640                     m.append(g1);
641                     TNode g2 = doc.createG();
642                     g2.append(cursor);
643                     m.append(g2);
644                     frames.push(Frame(entry));
645                   }
646                 else if (entry.leftOpen)
647                   {
648                     assert(parent.isG());
649                     TNode g = doc.createG();
650                     g["left-open"] = "1";
651                     g.append(parent.first(), m);
652                     m.append(g);
653                     advance(m);
654                   }
655                 else if (entry.rightOpen)
656                   {
657                     assert(entry.pattern.empty());
658                     assert(parent.isG());
659                     TNode g = doc.createG();
660                     g.append(cursor);
661                     m.append(g);
662                     frames.push(Frame(entry));
663                   }
664                 else if (!entry.pattern.empty())
665                   {
666                     frames.push(Frame(entry));
667                     if (entry.paramDelimited(0))
668                       {
669                         TNode g = doc.createG();
670                         m.append(g);
671                         g.append(cursor);
672                       }
673                     else
674                       m.append(cursor);
675                   }
676                 else
677                   {
678                     // it's an empty macro
679                     advance(m);
680                   }
681                 return true;
682               }
683             else if (!entry.pattern.size() && !entry.rightOpen && !entry.leftOpen)
684               {
685                 // a macro with no arguments and no right open and no left open, can be child of anything
686                 TNode m = doc.createC(name, nextId++);
687                 cursor.replace(m);
688                 advance(m);
689                 return true;
690               }
691             else
692               {
693                 // a macro with arguments or a rightOpen or leftOpen macro must be a group's child
694                 logger.warning("ignored token: this macro should be in a group");
695                 return false;
696               }
697           }
698           break;
699         case TDictionary::UNDEFINED:
700           {
701             logger.warning("using undefined macro " + name);
702             TNode m = doc.createC(name, nextId++);
703             cursor.replace(m);
704             advance(m);
705             return true;
706           }
707           break;
708         default:
709           {
710             //assert(0);
711             logger.warning("ignored token");
712             return false;
713           }
714         }
715     }
716 }
717
718 std::string
719 TPushParser::drop_prev_token(bool special)
720 {
721   assert(cursor.prev());
722   assert(cursor.parent());
723   TNode prev = cursor.prev();
724   assert(prev.isT());
725  
726   DOM::UCS4String ucs4val = prev.element().getAttribute("val");
727   bool macro = prev.element().hasAttribute("name");
728   std::string utf8name;
729   if (macro) utf8name = prev.element().getAttribute("name");
730   
731   cursor.remove();
732   prev.replace(cursor);
733   
734   if (cursor.parent().isC())
735     {
736       // in this case we have removed an element of a MACRO. 
737       // we can assert that this element was a non delimited argument
738       assert(!frames.empty());
739       Frame& frame = frames.top();
740       assert(frame.pos > 0);
741       frame.pos--;
742     }
743
744   if ((ucs4val.length() > 1))
745     {
746       if (!macro)
747         {
748           // in this case we can return the content of ucs4val, but we have 
749           // to convert it in a utf8
750           DOM::GdomeString gdsval(ucs4val);
751           std::string utf8val(gdsval);
752           switch (utf8val[utf8val.length() - 1])
753             {
754             case '-':
755             case '_':
756               return (special) ? std::string(utf8val, 0, utf8val.length() - 1) + "\\" : std::string(utf8val, 0, utf8val.length() - 1);
757             default: return (special) ? "" : std::string(utf8val, 0, utf8val.length() - 1);
758             }
759         }
760       else
761         {
762           // in this case, the content of val could be in unicode, 
763           // but we have the attribute name, which doesn't contain character not representable 
764           // with a byte.
765           return (special) ? "\\" + utf8name : "";
766         }
767     }
768   else if (macro && special) return "\\" + utf8name;
769   else return "";
770 }
771
772 std::string
773 TPushParser::drop_prev_script(bool special)
774 {
775   // this method deletes an sp or an sb preceding the cursor
776   assert(cursor.prev());
777   assert(cursor.parent());
778   TNode prev = cursor.prev();
779   assert(prev.is("sp") || prev.is("sb"));
780   cursor.remove();
781   prev.append(cursor);
782   // we can invoke the drop_prev, because a sp (sb) MUST have two children
783   // but we cannot invoke do_drop_script because it assumes when called, the first 
784   // child has been removed yet.
785   if (cursor.prev().isG() && !prev.hasId())
786     {
787       // in this case, the user has inserted a sequence of '.
788       // Hence, we force a normal deletion, because the behavior must be the same 
789       // for the two kind of deletion
790       return drop_prev(false);
791     }
792   else return drop_prev(special);
793 }
794
795 std::string
796 TPushParser::drop_prev_group(bool special)
797 {
798   assert(cursor.prev() && cursor.prev().isG());
799   TNode parent = cursor.parent();
800   TNode prev = cursor.prev();
801   cursor.remove();
802   prev.append(cursor);
803
804   if (parent.isC() && prev.hasId())
805     {
806       // this previous group is a macro's argument. Entering inside it means that
807       // this argument becomes incomplete. Hence, we have to decrement the member pos.
808       assert(!frames.empty());
809       frames.top().pos--;
810     }
811
812   if (special) return "";
813   else
814     {
815       // a group could have no children, so the drop_prev is not appropriate
816       // so, this method is not equivalent to the one above
817       return do_drop(special);
818     }
819 }
820
821 std::string
822 TPushParser::drop_prev_macro(bool special)
823 {
824   assert(cursor.parent());
825   assert(cursor.prev());
826   TNode prev = cursor.prev();
827   assert(prev.isC());
828
829   std::string macro_name = prev.nameC();
830
831   TNode parent = cursor.parent();
832   
833   const TDictionary::Entry& entry = dictionary.find(prev["name"]);
834   
835   if (!entry.defined())
836     {
837       // In this case, with a normal deletion, we completely remove the macro.
838       // With a special deletion, we remove the last character of the macro's name.
839       cursor.remove();
840       prev.replace(cursor);
841       if (cursor.parent().isC())
842         {
843           // we have removed a macro's child
844           assert(!frames.empty());
845           frames.top().pos--;
846         }
847       if (special) return "\\" + macro_name.erase(macro_name.length() - 1, 1); // we remove the last char, because an undefined macro's name is visible
848       return "";
849     }
850   else
851     {   
852       // we start to remove a MACRO. Different actions must be taken, based on the nature 
853       // of the MACRO. In some cases, we can't remove the MACRO immediately, in other
854       // cases it's correct. In the first set of cases, we have to update the stack, pushing
855       // a frame in it with a correct value of pos, in the 
856       // second one, we must not push a frame in the stack
857       
858       if (entry.rightOpen)
859         {
860           // In this fragment of code we also handle the leftOpen && rightOpen MACRO.
861           // since the control element is rightOpen, the cursor should be placed after 
862           // the last child of the control element's last child, and than, we try to remove something.
863           // A frame MUST be pushed in the stack, because we dont' know if the following actions 
864           // will completely remove the MACRO.
865           frames.push(Frame(entry));
866
867           // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
868           assert(prev.last().isG() && !prev.last().hasId());
869           
870           cursor.remove();
871           prev.last().append(cursor);
872
873           if (special) return "";
874           else
875             {
876               // the drop_prev is not appropriate, because the last child of the MACRO could have no children
877               return do_drop_phantom_group(special);
878             }
879         }
880       else if (entry.leftOpen)
881         {
882           // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
883           // In this case, we do not have to push a frame in the stack, because we remove the 
884           // MACRO immediately, substituting it with the content of the phantom group.
885           // We could remove the last child of the phantom group, but
886           // it's not clear if it's the correct behavior of the graphical deletion.
887           // At the moment, to give a standard behavior, we remove the last element.
888           // With a special deletion, we do not remove it.
889           assert(prev.first());
890           assert(prev.first().isG());
891           assert(prev.first() == prev.last());
892           
893           TNode g = prev.first();
894           if (g.size())
895             {
896               // in this case, the phantom group has at least one child, so we can call the 
897               // TNode::replace.
898               g.remove();
899               prev.replace(g.first(), TNode());
900               parent.append(cursor);
901               if (special) return "\\" + macro_name;
902               else return do_drop(special);
903             }
904           else
905             {
906               // otherwise, the phantom group has no children, so we remove it, also the MACRO.
907               cursor.remove();
908               g.remove();
909               prev.replace(cursor);
910               if (special) return "\\" + macro_name;
911               else
912                 {
913                   // Once removed this empty macro, we could try to remove something else.
914                   // This would be justified by the fact that, generally, an empty macro gives no visual information 
915                   // about it.
916                   return do_drop(special); // special is false
917                 }
918             }
919         }
920       else if (!entry.pattern.empty())
921         {
922           // we have to start to remove a MACRO which accepts arguments.
923           // If the MACRO accepts arguments, the MACRO has at least one child     
924           assert(prev.size() >= 1);
925
926           // Differnt actions must be taken, based on the nature of the last child
927           // of the MACRO. We have to distinguish the case in which it's a delimited argument,
928           // frome the one in which it's a not delimited argument.
929           if (prev.last().isG() && !prev.last().hasId())
930             {
931               if (special)
932                 {
933                   // in this case, we have to start removing the last delimiter
934                   frames.push(Frame(entry, entry.pattern.size() - 2));
935                   
936                   cursor.remove();
937                   prev.last().append(cursor);
938
939                   std::string last_del = entry.pattern[entry.pattern.size() - 1].value;
940
941                   return "\\" + last_del;
942                 }
943               else
944                 {
945                   // the last argument of the MACRO is a delimited argumet. We ideally remove 
946                   // the sequence of delimiters
947                   cursor.remove();
948                   prev.last().append(cursor);
949                   //  we have to push a frame with a correct value of pos
950                   assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
951                   
952                   unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
953                   unsigned p = entry.pattern.size() - sequence_length - 1;
954                   // now, p is the correct value of pos, and we can push the frame.
955                   frames.push(Frame(entry, p));
956                   
957                   // To give a standard behavior to the graphical deletion, we remove the last 
958                   // element of the macro. Since we are in a phantom group, we can invoke the 
959                   // do_drop_phantom_group(special).
960                   return do_drop_phantom_group(special);
961                 }
962             }
963           else
964             {
965               // in this case, the last child of the MACRO is not a delimited argument, so we try 
966               // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not. 
967               cursor.remove();
968               if (entry.table == 1 && prev.last().is("row"))
969                 {
970                   // in this case the cursor has to be appended to the group associated to 
971                   // the last cell of the last row of the table
972                   assert(prev.last().last().is("cell") && prev.last().last().first().isG());
973                   prev.last().last().first().append(cursor);
974                   
975                   // we have to push a frame in the stack. Since tables has a pattern size = 1, we have to 
976                   // set pos at 0, because appending the cursor to the last cell means that this argument 
977                   // is not whole inserted.
978                   // We don't call frames.push(Frame(entry)), because it incoditionaly set pos at 0. The 
979                   // following line is more general
980                   frames.push(Frame(entry, entry.pattern.size() - 1));
981                   if (special)
982                     {
983                       // to type a table with rows and cells, the user had typed a 
984                       // "{", and to exit from it, the user had inserted a "}".
985                       // Since we are in a special deletion, we just idealy remove the "}"
986                       return "";
987                     }
988                   else return do_drop_phantom_group(special);
989                 }
990               else
991                 {
992                   // we push a frame in the stack with a correct value of member pos.
993                   // This correct value is the size of the pattern - 1, because we have been started to delete 
994                   // a MACRO. It means that all of the MACRO's arguments have been inserted, but 
995                   frames.push(Frame(entry, entry.pattern.size()));
996                   prev.append(cursor);
997                   return drop_prev(special);
998                 }
999
1000             } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
1001
1002         } // end of if (!entry.pattern.empty())
1003       else
1004         {
1005           // if we are here, the MACRO preceding the cursor, is !(rightOpen || leftOpen),
1006           // and has no pattern. It means that it has no children.
1007           // We can replace it with the cursor
1008           assert(prev.size() == 0);
1009           cursor.remove();
1010           prev.replace(cursor);
1011           if (cursor.parent().isC())
1012             {
1013               // we have removed an empty macro, which was a non delimited argument of a macro.
1014               // We have to decrement pos
1015               assert(!frames.empty());
1016               frames.top().pos--;
1017             }
1018
1019           if (special) return "\\" + macro_name;
1020           else return "";
1021                   
1022           // now we could start to remove something else. This behavior would be justified by the 
1023           // fact that, generally, an empty MACRO gives no visual information about it.
1024           // To adopt this behavior, just remove the comment to the following instruction
1025           // return do_drop(special);
1026         }
1027     } // end of defined MACRO
1028
1029 }
1030
1031 std::string
1032 TPushParser::drop_prev(bool special)
1033 {
1034   // if in this function, the prev of cursor does exist, also the parent and we want a graphical deletion.
1035   
1036   assert(cursor.prev());
1037   assert(cursor.parent());
1038
1039   TNode prev = cursor.prev();
1040
1041   if (prev.isT())
1042     {
1043       return drop_prev_token(special);
1044     }
1045   else if (prev.isSp() || prev.isSb())
1046     {
1047       return drop_prev_script(special);
1048     }
1049   else if (prev.isG())
1050     {
1051       return drop_prev_group(special);
1052     }
1053   else if (prev.isC())
1054     {
1055       // here, we also treat the case in which the MACRO is a table
1056       return drop_prev_macro(special);
1057     }
1058   else 
1059     {
1060       // not handled. Future cases...
1061       return "";
1062     }
1063
1064 } // end of method
1065
1066 void
1067 TPushParser::rgreplace_father()
1068 {
1069   // this method MUST only be invoked, when the cursor
1070   // is the only child of a group with id. This function 
1071   // replaces the group with the cursor. But if the new parent
1072   // is a group with id and the cursor is the only child of the 
1073   // group, the new parent is replaced...and so on.
1074   // r stands for recursive, g stands for graphical.
1075   assert(cursor.parent());
1076   assert(cursor.parent().isG() && cursor.parent().hasId());
1077
1078   TNode parent = cursor.parent();
1079
1080   while (parent.isG() && parent.hasId() && (parent.first() == cursor))
1081     {
1082       parent.replace(cursor);
1083       parent = cursor.parent();
1084     }
1085 }
1086
1087 std::string
1088 TPushParser::do_drop_script(bool special)
1089 {
1090   // If we are here, the cursor is child of a script (sp or sb) and 
1091   // this means that a prev does exist and that there is one and only one 
1092   // element preceding the cursor. The sp's (or sb's) parent 
1093   // MUST NOT be a MACRO.
1094   // The element preceding the cursor is the base of the script.
1095
1096   assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
1097   TNode parent = cursor.parent();
1098           
1099   assert(parent.size() == 2);
1100   assert(parent.parent() && !parent.parent().isC());
1101   
1102   TNode prev = cursor.prev();
1103   cursor.remove();
1104   if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
1105     {
1106       // in this case, the script's base is a group with no elements, so 
1107       // we have to remove the entire MACRO, replacing it with the cursor.
1108       // This situation occurs when the user had typed something like this
1109       //   $....{}^
1110       // or this 
1111       //   $^
1112       // or this
1113       //   $...{^
1114       //
1115       if (special && prev.hasId())
1116         {
1117           // in this case, the user has typed: ...{}^
1118           // hence we idealy remove the ^
1119           parent.replace(prev);
1120           prev.parent().append(cursor);
1121           return "";
1122         }
1123       else if (!prev.hasId())
1124         {
1125           // we idealy remove the ^, but the phantom group 
1126           // has to be removed, also
1127           prev.remove();
1128           parent.replace(cursor);
1129           return "";
1130         }
1131       else
1132         {
1133           prev.remove();
1134           parent.replace(cursor);
1135           
1136           // since the script had no children, we can try to remove something else.
1137           // Since we don't know who is cursor's parent, and who is cursor's preceding 
1138           // element, we invoke the do_drop()
1139           return do_drop(special);
1140         }
1141     }
1142   else
1143     {
1144       // in this case, the prev has to replace the script.
1145       parent.replace(prev);
1146       prev.parent().append(cursor);
1147       // now prev have a preceding element
1148       assert(cursor.parent().size() > 1);
1149
1150       if (special) return "";
1151       else
1152         {
1153           // to give a standard behavior, we try to remove the element, which was 
1154           // the script's base.
1155           return do_drop(special);
1156         }
1157     }
1158   
1159 } // end of method do_drop_script
1160
1161 std::string
1162 TPushParser::do_drop_macro(bool special)
1163 {
1164   // If we are here, the cursor is a child of a MACRO and this means
1165   // that there is an open frame for the control element
1166   // and this element is closed at either side (no leftOpen no rightOpen)
1167   // and the MACRO is waiting for a not delimited argument, so 
1168   // we can assert that frame.entry.pattern.size() >= 1
1169   assert(cursor.parent() && cursor.parent().isC());
1170   TNode parent = cursor.parent();
1171
1172   // this string is useful iff we have a special deletion.
1173   std::string macro_name = parent.nameC();
1174   
1175   assert(!frames.empty());
1176   Frame& frame = frames.top();
1177   assert(frame.entry.pattern.size() >= 1);
1178
1179   // we have to take different actions, based on if a preceding element exists 
1180   // or not
1181   TNode prev = cursor.prev();
1182   if (!prev)
1183     {
1184       // in this case, a prev does not exist, so the actions of deleting means 
1185       // that we have to remove the MACRO. So we have to pop the stack.
1186       assert(frame.pos == 0);
1187       
1188       parent.replace(cursor);
1189       frames.pop();
1190
1191       if (special) return "\\" + macro_name;
1192       else
1193         {
1194           // Since the macro had no children and this is a graphical deletion, we try 
1195           // to remove something else
1196           return do_drop(special);
1197         }
1198     }
1199   else
1200     {
1201       // a prev does exist, we have to control if it's a delimited argument or not.
1202       if (prev.isG() && !prev.hasId())
1203         {
1204           // in this case, prev is a delimited argument, so we have 
1205           // to ideally remove the sequence of delimiters
1206           Frame& frame = frames.top();
1207           assert(frame.pos > 1);
1208           cursor.remove();
1209           prev.append(cursor);
1210           assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1211
1212           if (special)
1213             {
1214               // in this case we have to start removing the last delimimeter.
1215               // It means that we return in a situation where the user has not entirely 
1216               // inserted the delimited argument. So, we have to decrement frame.pos of 
1217               // two units: the delimiter and the actual argument
1218               std::string last_del = frame.entry.pattern[frame.pos - 1].value;
1219               frame.pos = frame.pos - 2;
1220               return "\\" + last_del;
1221             }
1222           else
1223             {
1224               // these 3 lines of code update the member pos.
1225               unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1226               assert(sequence_length);
1227               frame.pos = frame.pos - sequence_length - 1;
1228               
1229               // since it's a graphical deletion, we have to remove the current preceding element.
1230               // We don't invoke the drop_prev(), because a do_drop_phantom_group is more general.
1231               return do_drop_phantom_group(special);
1232             }
1233         }
1234       else
1235         {
1236           // the prev is not a delimited argument, so we have to try to remove it. 
1237           // We "try", because the prev might be something that 
1238           // a simple deletion cannot remove completely
1239           return drop_prev(special);
1240         }
1241     }
1242
1243 }
1244
1245 std::string
1246 TPushParser::do_drop_groupId(bool special)
1247 {
1248   // if we are here, the cursor's parent is a group with Id
1249   assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
1250   TNode parent = cursor.parent();
1251
1252   // we have to take different actions based on if the cursor has a preceding 
1253   // element or not
1254   TNode prev = cursor.prev();
1255   if (prev)
1256     {
1257       // the cursor has a preceding element, so we try to remove it
1258       if (special) return drop_prev(special);
1259       else
1260         {
1261           std::string str = drop_prev(special);
1262
1263           // We control if the group has to be removed, because the cursor 
1264           // might be the only element of the group.
1265           // But we have to be careful, because drop_prev could change the TML tree 
1266           // more than we think...parent could no longer exist! 
1267           parent = cursor.parent();
1268           if ((parent.first() == cursor) && parent.isG() && parent.hasId())
1269             rgreplace_father();
1270       
1271           return str;
1272         }
1273     }
1274   else
1275     {
1276       // the cursor has no preceding elements, so we have to remove the 
1277       // group.
1278       if (special)
1279         {
1280           parent.replace(cursor);
1281           return "";
1282         }
1283       else
1284         {
1285           rgreplace_father();
1286           // we have to re-start the process, because it' a graphical deletion
1287           return do_drop(special);
1288         }
1289     }
1290
1291 } // end of method do_drop_groupId()
1292
1293 std::string
1294 TPushParser::do_drop_phantom_group(bool special)
1295 {
1296   // if we are here, the cursor MUST be a child of a 
1297   // phantom group.
1298   assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
1299
1300   TNode parent = cursor.parent();
1301
1302   // now we have to control if the cursor has a preceding element or not
1303   TNode prev = cursor.prev();
1304   if (prev)
1305     {
1306       if (parent.parent() && parent.parent().isC())
1307         {
1308           // there is a frame in the stack
1309           assert(!frames.empty());
1310           if (frames.top().entry.pattern.size())
1311             {
1312               Frame& frame = frames.top();
1313               if (special)
1314                 {
1315                   // we are in a delimited argument. If the user has inserted a proper subset of the 
1316                   // delimiters'sequence, we start to remove the previous delimiter. Start to remove 
1317                   // a delimiter means that that delimiter must be removed from the count of inserted delimiters.
1318                   // It means that we have to decrement the member pos.
1319                   if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1320                     {
1321                       std::string del = frame.entry.pattern[frame.pos].value;
1322                       frame.pos--;
1323                       return "\\" + del;
1324                     }
1325                 }
1326               else
1327                 {
1328                   // we are in a delimited argument. If the user has inserted a proper subset of the delimiters'sequence, 
1329                   // we have to remove the portion the user has inserted.
1330                   while (frame.entry.pattern[frame.pos].category != TToken::PARAMETER) frame.pos--;
1331                 }
1332             }
1333         }
1334       
1335       // the cursor has a preceding element, so we try to remove it
1336       std::string str = drop_prev(special);
1337
1338       if (special) return str;
1339       else
1340         {
1341           // now we have to control the parent, to handle the case of primes. But we have returned from a drop_prev(), which
1342           // could change the TML tree. So not asssuming that cursor's parent is unchanged is convenient.
1343           parent = cursor.parent();
1344           if (parent.isG() && !parent.hasId() && (parent.size() == 1) && parent.parent().isSp())
1345             {
1346               // in this case the drop_prev has removed the only element preceding the cursor.
1347               // Since the phantom group is an sp's child, the user has removed all \' in the 
1348               // phantom group.
1349               // Now we have some possibilities:
1350               //   - we can replace the phantom group with the cursor, giving the user the chance to insert a new 
1351               //     exponent
1352               //   - we can remove the phantom group and the sp element, recreating the state before the user inserted the first
1353               //     prime.
1354               // At the moment we implement the second one.
1355               assert(parent.parent().size() == 2);
1356               TNode gparent = parent.parent();
1357               TNode base = gparent.first();
1358               cursor.remove();
1359               parent.remove();
1360               gparent.replace(base);
1361               // now base's parent is no more gparent
1362               base.parent().append(cursor);
1363           
1364               return str;
1365             }
1366           else if (parent.isG() && !parent.hasId() && parent.parent().isSp())
1367             {
1368               // in this case we have to place the cursor after the sp element
1369               cursor.remove();
1370               assert(parent.parent().parent());
1371               parent.parent().parent().append(cursor);
1372               return str;
1373             }
1374           else return str;
1375         }
1376     }
1377   else
1378     {
1379       // in this case the cursor is the only element of the phantom group,
1380       // so we have to remove it. But, a phantom group has a special role, 
1381       // so we have to control the grand father of the cursor.
1382       TNode gfather = parent.parent();
1383       if (!gfather)
1384         {
1385           // If here, the TML tree is in an inconsistent state
1386           logger.error("TML tree in a inconsistent state");
1387           return "";
1388         }
1389       else if (gfather.isC())
1390         {
1391           // in this case the phantom group is child of a MACRO.
1392           // We have to control the nature of this MACRO.
1393           assert(!frames.empty());
1394           Frame& frame = frames.top();
1395
1396           // this variable is useful in a special deletion
1397           std::string macro_name = gfather.nameC();
1398           
1399           if (frame.entry.leftOpen && frame.entry.rightOpen)
1400             {
1401               // in this case, the cursor'parent is in the second and last child 
1402               // of the MACRO. We can assert that the grand father has two 
1403               // children, which are both phantom groups
1404               assert(gfather.size() == 2);
1405               assert((gfather.last() == parent) && (gfather.first().isG() && !gfather.first().hasId()));
1406               assert(frame.pos == 0);
1407               
1408               TNode ggfather = gfather.parent();
1409               assert(ggfather);
1410               cursor.remove();
1411               parent.remove();
1412               // we have to replace the gfather with the elements of its first child, but this group may have no 
1413               // children.
1414               if (gfather.first().size())
1415                 {
1416                   gfather.replace(gfather.first().first(), TNode());
1417                   ggfather.append(cursor);
1418                 }
1419               else
1420                 {
1421                   // in this case, the MACRO has to be replaced with the cursor
1422                   gfather.first().remove();
1423                   gfather.replace(cursor);
1424                 }
1425               // now we have the situation preceding the insertion of the leftOpen and rightOpen MACRO.
1426               // this MACRO no longer exists.
1427               frames.pop();
1428
1429               if (special) return "\\" + macro_name;
1430               else
1431                 {
1432                   // to give a standard behavior to the graphical deletion, we call the do_drop.
1433                   return do_drop(special);
1434                 }
1435             }
1436           else if (frame.entry.rightOpen)
1437             {
1438               // the user has inserted a rightOpen MACRO, and now, this MACRO has no children (excluding the 
1439               // phantom group), so we remove the MACRO. 
1440               // We can assert that cursor's parent is the only child of the MACRO
1441               assert(gfather.size() == 1);
1442               assert(frame.pos == 0);
1443               cursor.remove();
1444               parent.remove();
1445               gfather.replace(cursor);
1446               
1447               // now we have the situation preceding the rightOpen MACRO, so we have to pop the frame
1448               frames.pop();
1449
1450               if (special) return "\\" + macro_name;
1451               else
1452                 {
1453                   // to give a standard behavior to the graphical deletion, we call the do_drop.
1454                   return do_drop(special);
1455                 }
1456               
1457             }
1458           else if (frame.entry.leftOpen)
1459             {
1460               // this situation will never occur.
1461               logger.error("the parser has generated a wrong TML tree");
1462               return "";
1463             }
1464           else if (!frame.entry.pattern.empty())
1465             {
1466               // the MACRO accepts arguments, and the phantom group in which 
1467               // the cursor is, rappresents a delimited argument.
1468               // We have to control if the cursor's parent has a preceding element, 
1469               // or not.
1470               TNode uncle = parent.prev();
1471               if (!uncle)
1472                 {
1473                   // the parent is the only element of the MACRO. 
1474                   // we can assert that frame.pos == 0.
1475                   // In this case we can replace the MACRO with the cursor
1476                   assert(frame.pos == 0);
1477                   cursor.remove();
1478                   parent.remove();
1479                   gfather.replace(cursor);
1480                   frames.pop();
1481
1482                   if (special) return "\\" + macro_name;
1483                   else
1484                     {
1485                       // once we have replaced the empty macro with the cursor, we can remove
1486                       // something else
1487                       return do_drop(special);
1488                     }
1489                 }
1490               else
1491                 {
1492                   // the parent has a preceding element. Now we have 
1493                   // to control if the uncle is a delimited argument or not.
1494                   if (uncle.isG() && !uncle.hasId())
1495                     {
1496                       // cursor's uncle is a delimited argument
1497                       cursor.remove();
1498                       parent.remove();
1499                       uncle.append(cursor);
1500                       if (special)
1501                         {
1502                           // we have to start removing the last delimiter of the delimited 
1503                           // argument.
1504                           std::string last_del = frame.entry.pattern[frame.pos - 1].value;
1505                           frame.pos = frame.pos - 2;
1506                           return "\\" +  last_del; 
1507                         }
1508                       else
1509                         {
1510                           // the  uncle is a delimited argument. So we have to ideally
1511                           // remove the sequence of delimiters.
1512                           assert(frame.pos > 1);
1513                           unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1514                           assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1515                           assert(sequence_length);
1516                           // sequence_length is the length of the delimiters sequence which separates
1517                           // the current parameter and the previous parameter
1518                           frame.pos = frame.pos - sequence_length - 1;
1519
1520                           // once removed the sequnce of delimiters, we can start to remove the actual 
1521                           // parameter. We can call the do_drop_phantom_group() because a delimited argument
1522                           // is always a phantom group's child
1523                           return do_drop_phantom_group(special);
1524                         }
1525                     }
1526                   else
1527                     {
1528                       // the uncle is a not delimited argument, so we try to remove it.
1529                       cursor.remove();
1530                       parent.replace(cursor);
1531                       parent = cursor.parent(); // we update the parent (it should be the MACRO)
1532                       assert(parent.isC());
1533
1534                       // now we try to remove the uncle (now it' the preceding element)
1535                       return drop_prev(special);
1536                     }
1537                 } // this is the else's end, that handles the case in which an uncle exists
1538             } // end of if (!frame.entry.pattern.empty())
1539           else
1540             {
1541               // the entry has no arguments, is not rightOpen and is not leftOpen.
1542               logger.error("TML tree in a strange state");
1543               return "";
1544             }
1545         } // end of if (gfather.isC())
1546       else if (gfather.is("cell"))
1547         {
1548           // A table is a control sequence, so there is a frame in the stack
1549           assert(!frames.empty());
1550           assert(frames.top().pos == 0);
1551           assert(frames.top().entry.table == 1);
1552           
1553           // a cell MUST be a row's child, which in turn is a table's child 
1554           assert(gfather.parent() && gfather.parent().is("row") && gfather.parent().parent());
1555
1556           // this variable is useful to handle the special deletion
1557           std::string table_name = gfather.parent().parent().nameC();
1558           
1559           TNode row = gfather.parent();
1560           
1561           // in this case the cell has no element, so the user wants to delete this cell.
1562           TNode prev_cell = gfather.prev();
1563           cursor.remove();
1564           parent.remove();
1565           gfather.remove();
1566           // now the cell no longer exists
1567
1568           if (!prev_cell)
1569             {
1570               // in this case, the cell was the only cell in the row.
1571               // So, we assume that the user wants to delete the entire row.
1572               TNode table = row.parent();
1573               TNode prev_row = row.prev();
1574               row.remove();
1575               
1576               if (!prev_row)
1577                 {
1578                   if (special)
1579                     {
1580                       // Since there was a cell (and a row), the user has typed a "{" to 
1581                       // We ideally remove this character.
1582                       table.append(cursor);
1583                       return "";
1584                     }
1585                   else
1586                     {
1587                       // the row was the only child of the table. 
1588                       // so we have to delete the entire table
1589                       assert(table.parent());
1590                       TNode parent_table = table.parent();
1591                       table.remove();
1592                       frames.pop();
1593                       parent_table.append(cursor);
1594                       return "";
1595                     }
1596                 }
1597               else
1598                 {
1599                   // there are other rows (one or more)
1600                   assert(prev_row.is("row"));
1601                   assert(prev_row.last());
1602                   TNode last_cell = prev_row.last();
1603                   assert(last_cell.is("cell"));
1604                   assert(last_cell.size() == 1);
1605                   assert(last_cell.first().isG() && !last_cell.first().hasId());
1606                   last_cell.first().append(cursor);
1607                   // Since cells and rows are separated by spaces and CRs 
1608                   // (and the user can see this spaces and CRs), a special deletion 
1609                   // is equivalent to a normal deletion
1610                   return "";
1611                 }
1612             } // end of if (!prev_cell)
1613           else
1614             {
1615               // being here means that there is a previous cell,
1616               // so we append the cursor to group.
1617               assert(prev_cell.size() == 1);
1618               assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1619               prev_cell.first().append(cursor);
1620               return "";
1621             }
1622         } // end of if (gfather.is("cell"))
1623       else if (gfather.isSp())
1624         {
1625           // we cannot be here because a phantom group can be a Sp child only 
1626           // in two cases. If the user has typed somethong like:
1627           // $^
1628           // the cursor is not phantom group's child.
1629           // If the user has typed somethong like
1630           // ..''
1631           // In this case the sequence of ' is placed in a phantom group, 
1632           // which becomes the exponent of the script. But, the cursor is 
1633           // always outside the phantom group
1634           logger.error("TML tree in a strange state");
1635           return "";
1636         }
1637       else if (gfather.is("math"))
1638         {
1639           // in this case we ignore the user's will of deleting
1640           // but we could also decide to remove the math mode.
1641           logger.warning("nothing to delete");
1642           return "";
1643         }
1644       else
1645         {
1646           // cursor's grand father is undefined
1647           logger.error("TML tree is in an unknown state");
1648           return "";
1649         }
1650     } // end of the else of the if (prev)
1651
1652 }
1653
1654
1655 std::string
1656 TPushParser::do_drop(bool special)
1657 {
1658   // we have to handle the case in wich the cursor has a parent or not
1659   if (!cursor.parent())
1660     {
1661       // it's not a good situation...at the moment we do not take actions
1662       logger.error("TML tree not well structured");
1663       return "";
1664     }
1665   else
1666     {
1667       // a parent exists. We have to take differnt actions, based on the nature of 
1668       // the parent
1669       TNode parent = cursor.parent();
1670       if (parent.is("math"))
1671         {
1672           // we ca do two thing...we can remove the math mode (it implies controlling the display attribute), we can do nothing
1673           // At the moment, the user's will of deleting is simply ignored
1674           logger.warning("nothing to delete");
1675           return "";
1676         }
1677       else if (parent.isG())
1678         {
1679           // the cursor's parent is a group. We have to control if it's a phantom group or not
1680           if (parent.hasId())
1681             {
1682               return do_drop_groupId(special);
1683             }
1684           else
1685             {
1686               return do_drop_phantom_group(special);
1687             }
1688         } // end of parent is group
1689       else if (parent.isC())
1690         {
1691           return do_drop_macro(special);
1692         } // end of parent is a MACRO
1693       else if (parent.isSp() || parent.isSb())
1694         {
1695           return do_drop_script(special);
1696         } // end of parent is sp or sb
1697     } // end of the else which consider the case in which parent exists
1698   
1699 } // end of method do_drop
1700
1701 bool
1702 TPushParser::process(const TToken& token)
1703 {
1704   switch (token.category)
1705     {
1706     case TToken::BEGIN: return do_begin();
1707     case TToken::END: return do_end();
1708     case TToken::SHIFT: return do_shift();
1709     case TToken::ALIGN: return do_align();
1710     case TToken::EOL: return do_eol();
1711     case TToken::PARAMETER: return do_parameter(token.value);
1712     case TToken::SUPERSCRIPT: return do_superscript();
1713     case TToken::SUBSCRIPT: return do_subscript();
1714     case TToken::IGNORABLE_SPACE: return do_ignorablespace(token.value);
1715     case TToken::SPACE: return do_space(token.value);
1716     case TToken::LETTER: return do_letter(token.value);
1717     case TToken::DIGIT: return do_digit(token.value);
1718     case TToken::OTHER: return do_other(token.value);
1719     case TToken::ACTIVE: return do_active(token.value);
1720     case TToken::COMMENT: return do_comment();
1721     case TToken::CONTROL: return do_control(token.value);
1722     }
1723 }
1724
1725 void
1726 TPushParser::push(const TToken& token)
1727 {
1728   TNode parent = cursor.parent();
1729   // If the cursor has no parent then it is detached from the editing
1730   // tree, which means this token will be ignored
1731
1732   if (parent)
1733     // If the parent is a phantom group and the grand-parent is a
1734     // control sequence, there are two cases:
1735     // a. we are parsing a delimited argument of a entry
1736     // b. we are parsing a side of a right- or left-open entry
1737     if (parent.isG() && !parent.hasId() && parent.parent().isC())
1738       {
1739         // There must be an open frame, for the grand-parent is a control sequence
1740         assert(!frames.empty());
1741         Frame& frame = frames.top();
1742         if (!frame.entry.pattern.empty())
1743           {
1744             // The entry pattern is not empty. By our conventions this means
1745             // the entry cannot be open at either end, hence we are parsing
1746             // a delimited argument
1747             assert(frame.pos + 1 < frame.entry.pattern.size());
1748             assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1749             if (frame.entry.pattern[frame.pos + 1] == token)
1750               {
1751                 // The token matches with a delimiter of the argument, 
1752                 // hence we increment the frame.pos
1753                 frame.pos++;
1754
1755                 if (frame.entry.lastDelimiter(frame.pos))
1756                   {
1757                     // this delimiter is the last one for the argumet, 
1758                     // so the argument is completed
1759                     cursor.remove();
1760                     advance(parent);
1761                   }
1762               }
1763             else
1764               {
1765                 // Delimiter mismatch.
1766                 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1767                   {
1768                     // in this case, there is a sequence of delimiters that delimitates
1769                     // the argument, and the user has correctly inserted a portion of this 
1770                     // sequence, but now has inserted a wrong delimiter.
1771                     // Here, there are some possibilities:
1772                     //   - ignore the token, and wait for the correct delimiter
1773                     //   - ignore the token, wait for the correct delimiter and emit an error
1774                     // At the moment, we implement the second possibily
1775                     logger.warning("it's not the correct delimiter...you have to type '" + frame.entry.pattern[frame.pos + 1].value + "'");
1776                   }
1777                 else
1778                   {
1779                     // in this case, the sequence of delimiters is composed of one 
1780                     // delimiter. It means that we have to process the token
1781                     process(token);
1782                   }
1783               }
1784           }
1785         else
1786           {
1787             // The entry pattern is empty, hence we are parsing a right-open
1788             // entry. What happens if we actually are in the left side?
1789             // This could happen only when re-editing an entered expression
1790             // We'll see...
1791             assert(frame.entry.rightOpen);
1792             process(token);
1793           }
1794       }
1795     else if (parent.isC())
1796       {
1797         // We are parsing a non-delimited argument entry
1798         // or a fixed token
1799         Frame& frame = frames.top();
1800         assert(frame.pos < frame.entry.pattern.size());
1801
1802         if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1803           {
1804             // As by the TeX parsing rules of undelimited parameters,
1805             // empty spaces are ignored
1806             if ((token.category != TToken::SPACE) && (token.category != TToken::IGNORABLE_SPACE)) process(token);
1807           }
1808         else if (frame.entry.pattern[frame.pos] == token)
1809           {
1810             // The token has been accepted
1811             frame.pos++;
1812             if (frame.pos < frame.entry.pattern.size() &&
1813                 frame.entry.paramDelimited(frame.pos))
1814               {
1815                 // If the next is a delimited argument we have to place
1816                 // the phantom group with the cursor inside
1817                 TNode g = doc.createG();
1818                 cursor.replace(g);
1819                 g.append(cursor);
1820               }
1821             else
1822               {
1823                 cursor.remove();
1824                 advance(parent);
1825               }
1826           }
1827         else
1828           {
1829             // There is a mismatch. Emit an error and ignore the token?
1830             logger.warning("ignored token: " + token.value);
1831           }
1832       }
1833     else
1834       process(token);
1835   else
1836     {
1837       logger.warning("ignored token");
1838     }
1839
1840   if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1841 }
1842
1843 std::string
1844 TPushParser::drop(bool special)
1845 {
1846   std::string str = do_drop(special);
1847   if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1848   return str;
1849 }
1850
1851 void
1852 TPushParser::advance(const TNode& node)
1853 {
1854   assert(node);
1855   
1856   if (!node.parent())
1857     {
1858       // this is an error
1859       logger.error("wrong TML tree");
1860     }
1861   else if (node.parent().isG())
1862     {
1863       TNode next = node.next();
1864       if (next) next.insert(cursor);
1865       else node.parent().append(cursor);
1866     }
1867   else if (node.parent().isC())
1868     {
1869       assert(!frames.empty());
1870       if ((frames.top().pos + 1 == frames.top().entry.pattern.size()) || (frames.top().entry.pattern.empty()))
1871         {
1872           // we are here when we have a right open macro, or the inserted element is the last one
1873           if (frames.top().entry.rightOpen)
1874             {
1875               // we have to remove the frame from the stack
1876               frames.pop();
1877               advance(node.parent().parent());
1878             }
1879           else
1880             {
1881               frames.pop();
1882               advance(node.parent());
1883             }
1884         }
1885       else if (frames.top().entry.paramDelimited(frames.top().pos + 1))
1886         {
1887           // the next argument is delimited, so we have to create a phantom group
1888           TNode g = doc.createG();
1889           g.append(cursor);
1890           node.parent().append(g);
1891           frames.top().pos++;
1892         }
1893       else
1894         {
1895           // the next argumet is not delimited, so we have to append the cursor
1896           // to the MACRO
1897           node.parent().append(cursor);
1898           frames.top().pos++;
1899         }
1900     }
1901   else advance(node.parent());
1902 }
1903
1904 void
1905 TPushParser::setCursorHint(const std::string& c)
1906 {
1907   if (cursor["val"] != c)
1908     {
1909       cursor["val"] = c;
1910       if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1911     }
1912 }
1913
1914 bool
1915 TPushParser::hideCursor()
1916 {
1917   if (hiddenCursor++ == 0)
1918     {
1919       cursor["visible"] = "0";
1920       if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1921       return true;
1922     }
1923   else
1924     return false;
1925 }
1926
1927 bool
1928 TPushParser::showCursor()
1929 {
1930   if (hiddenCursor > 0 && --hiddenCursor == 0)
1931     {
1932       cursor["visible"] = "1";
1933       if (factory && doc.dirtyNode() && !frozen()) factory->documentModified(doc);
1934       return true;
1935     }
1936   else
1937     return false;
1938 }
1939
1940 bool
1941 TPushParser::thaw()
1942 {
1943   if (APushParser::thaw() && factory && doc.dirtyNode())
1944     {
1945       factory->documentModified(doc);
1946       return true;
1947     }
1948   else
1949     return false;
1950 }