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