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