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