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