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