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