3 #include "TPushParser.hh"
4 #include "AMathMLFactory.hh"
6 TPushParser::TPushParser(ALogger& l, const TDictionary& d) : APushParser(l), dictionary(d)
11 TPushParser::TPushParser(ALogger& l, AMathMLFactory& f, const TDictionary& d) : APushParser(l, f), dictionary(d)
20 cursor = doc.create("cursor");
23 doc.root().append(cursor);
24 logger.verbosity(ALogger::Debug);
27 TPushParser::~TPushParser()
32 TPushParser::PRIME() const
34 const TDictionary::Entry entry = dictionary.find("prime");
35 if (entry.cls == TDictionary::OPERATOR) return entry.value;
40 TPushParser::do_begin()
42 TNode parent = cursor.parent();
43 if (parent.isC() && dictionary.find(parent.nameC()).table)
45 TNode row = doc.create("row");
46 TNode cell = doc.create("cell");
47 TNode g = doc.createG();
55 TNode g = doc.createG(nextId++);
64 TNode parent = cursor.parent();
65 if (parent && parent.isG() && parent.hasId())
67 // normal closing brace for an explicitly open group
71 else if (parent && parent.isG() && parent.parent() && parent.parent().is("cell"))
73 assert(!frames.empty());
74 // closing brace for a structure in which & or \cr have been used
75 TNode row = parent.parent().parent();
76 assert(row && row.is("row"));
77 TNode table = row.parent();
81 else if (parent && parent.isG() && !parent.hasId() && parent.parent())
83 // closing brace for a right-open macro (like \over)
84 // It's not sure that we want to exit from a phantom group with a
85 // '}', because, to enter in the phantom group the user didn't insert a
92 // In this case, there is a redundant '}', so I think we can ignore it and
94 logger.error("There so no corresponding'{'");
100 TPushParser::do_shift()
102 TNode parent = cursor.parent();
104 if (parent.is("tex"))
106 TNode math = doc.create("math", nextId++);
107 TNode g = doc.createG();
108 cursor.replace(math);
112 else if (parent.isG() && !parent.hasId() && parent.parent() && parent.parent().is("math"))
116 // there is something before the cursor, hence this is the
117 // closing math shift
118 if (parent.parent()["display"] != "1")
120 // one math shift is enough to close it
125 // we need two closing math shifts
126 //cursor.remove(); ??
127 parent.parent().append(cursor);
130 else if (parent.parent()["display"] != "1")
132 // there is nothing before the cursor, and the math is not
133 // in display mode, so this must be a double math shift
134 parent.parent()["display"] = "1";
138 parent.parent().append(cursor);
141 else if (parent.is("math"))
147 // In TeX, the user can type
149 // In this case, '$' character is correct.
150 // I don't think so, if user types
151 // $12+{a-b\over2a+b$
152 // because the group is not closed with a '}'
153 logger.error("ERROR: math shift");
158 TPushParser::do_align()
160 TNode parent = cursor.parent();
161 if (parent && parent.isG() && parent.hasId())
163 // alignment tab used for the first time inside a group
164 TNode row = doc.create("row");
165 TNode cell = doc.create("cell");
166 TNode g = doc.createG();
169 g.append(parent.first(), cursor);
171 else if (parent && parent.isG() && parent.parent().is("cell"))
173 // alignment tab used within a cell
174 TNode oldCell = parent.parent();
175 assert(oldCell && oldCell.is("cell"));
176 TNode row = oldCell.parent();
177 assert(row && row.is("row"));
178 TNode cell = doc.create("cell");
179 if (oldCell.next()) oldCell.next().insert(cell);
180 else row.append(cell);
181 TNode g = doc.createG();
187 logger.error("alignment tab used outside matrix");
192 TPushParser::do_eol()
194 //if (cursor.parent()) cursor.remove();
198 TPushParser::do_parameter(const std::string& p)
204 TPushParser::do_subscript()
206 TNode parent = cursor.parent();
209 TNode prev = cursor.prev();
212 TNode elem = doc.create("sb", nextId++);
213 TNode g = doc.createG();
214 cursor.replace(elem);
220 TNode elem = doc.create("sb", nextId++);
226 else if (parent.isSb() && cursor == parent[1])
228 if (parent["under"] == "1") logger.error("already under");
229 else parent["under"] = "1";
234 TPushParser::do_superscript()
236 TNode parent = cursor.parent();
239 TNode prev = cursor.prev();
242 TNode elem = doc.create("sp", nextId++);
243 TNode g = doc.createG();
244 cursor.replace(elem);
250 TNode elem = doc.create("sp", nextId++);
256 else if (parent.isSp() && cursor == parent[1])
258 if (parent["over"] == "1") logger.error("already over");
259 else parent["over"] = "1";
264 TPushParser::do_space(const std::string&)
266 // ? may be used to distinguish tokens in some mode?
270 TPushParser::do_letter(const std::string& s)
272 //TNode parent = cursor.parent();
273 TNode elem = doc.createI(s, nextId++);
274 cursor.replace(elem);
279 TPushParser::do_digit(const std::string& s)
281 TNode parent = cursor.parent();
282 TNode prev = cursor.prev();
283 if (prev && parent.isG() && prev.is("n"))
285 TNode elem = doc.createN(prev.value() + s, nextId++);
290 TNode elem = doc.createN(s, nextId++);
291 cursor.replace(elem);
297 TPushParser::isPrimes(const TNode& node) const
300 return node.isG() && node.last() && node.last().is("o") && node.last()["val"] == PRIME();
304 TPushParser::do_apostrophe()
306 if (cursor.parent() && cursor.parent().isG())
308 if (TNode prev = cursor.prev())
310 if (prev.isSp() && prev[1] && isPrimes(prev[1]))
311 prev[1].append(doc.createO(PRIME(), nextId++));
312 else if (prev.isSb() && prev[0] &&
313 prev[0].isSp() && prev[0][1] &&
314 isPrimes(prev[0][1]))
315 prev[0][1].append(doc.createO(PRIME(), nextId++));
318 TNode elem = doc.create("sp");
319 TNode g = doc.createG();
323 g.append(doc.createO(PRIME(), nextId++));
329 logger.error("you have to type one identifier before the ''");
339 TPushParser::do_other(const std::string& s)
347 cout << "TPushParser::do_other " << s << endl;
348 cout << "DOCUMENT: " << static_cast<GdomeNode*>(cursor.element().get_ownerDocument()) << endl;
349 TNode elem = doc.createT("o", s, nextId++);
350 cursor.replace(elem);
357 TPushParser::do_active(const std::string&)
363 TPushParser::do_comment()
371 TNode parent = cursor.parent();
372 if (parent && parent.isG() &&
373 parent.parent() && parent.parent().is("cell") &&
374 parent.parent().parent() && parent.parent().parent().is("row"))
376 TNode oldRow = parent.parent().parent();
378 TNode table = oldRow.parent();
380 TNode row = doc.create("row");
381 TNode cell = doc.create("cell");
382 TNode g = doc.createG();
383 if (oldRow.next()) oldRow.next().insert(row);
384 else table.append(row);
392 TPushParser::do_control(const std::string& name)
394 if (name == "cr") do_cr();
397 TNode parent = cursor.parent();
398 const TDictionary::Entry& entry = dictionary.find(name);
401 case TDictionary::IDENTIFIER:
403 TNode t = doc.createI(entry.value, nextId++);
409 case TDictionary::OPERATOR:
411 TNode t = doc.createO(entry.value, nextId++);
417 case TDictionary::NUMBER:
419 TNode t = doc.createN(entry.value, nextId++);
425 case TDictionary::MACRO:
427 TNode m = doc.createC(name, nextId++);
430 logger.debug("do_control: i have pushed a frame in the stack. Here is the pattern of the associated MACRO");
431 for (unsigned i = 0; i < entry.pattern.size(); i++)
432 logger.debug(entry.pattern[i].value);
434 if (entry.leftOpen && entry.rightOpen)
436 assert(entry.pattern.empty());
437 assert(parent.isG());
438 TNode g1 = doc.createG();
439 g1["left-open"] = "1";
440 g1.append(parent.first(), m);
442 TNode g2 = doc.createG();
445 frames.push(Frame(entry));
447 else if (entry.leftOpen)
449 assert(parent.isG());
450 TNode g = doc.createG();
451 g["left-open"] = "1";
452 g.append(parent.first(), m);
456 else if (entry.rightOpen)
458 assert(entry.pattern.empty());
459 assert(parent.isG());
460 TNode g = doc.createG();
463 frames.push(Frame(entry));
465 else if (!entry.pattern.empty())
469 frames.push(Frame(entry));
471 if (entry.paramDelimited(0))
473 TNode g = doc.createG();
482 // error, but we could handle this very easily
483 logger.error("error, but we could handle this easily");
489 case TDictionary::UNDEFINED:
491 logger.error("ERROR: using undefined macro ` + name '");
492 TNode m = doc.createC(name, nextId++);
504 TPushParser::gdelete_prev_token()
506 assert(cursor.prev());
507 assert(cursor.parent());
508 TNode prev = cursor.prev();
509 assert(prev.is("i") || prev.is("o") || prev.is("n"));
511 logger.debug("gdelete_prev_token: start");
513 // the control below is designed to handle the case in which val have more than one unicode character
514 DOM::UCS4String ucs4val(prev.element().getAttribute("val"));
515 if ((ucs4val.length() <= 1) || prev.element().hasAttribute("name"))
517 logger.debug("gdelete_prev_token: i have removed an element");
519 prev.replace(cursor);
521 if (cursor.parent().isC())
523 // in this case we have removed an element of a MACRO.
524 // we can assert that this element was a non delimited argument
525 assert(!frames.empty());
526 Frame& frame = frames.top();
527 assert(frame.pos > 0);
530 logger.debug("gdelete_prev_token: it was a MACRO's child so i have decremented the member pos of the associated frame");
535 ucs4val.erase(ucs4val.length() - 1, 1);
536 prev.element().setAttribute(DOM::GdomeString("val"), DOM::GdomeString(ucs4val));
539 logger.debug("gdelete_prev_token: bye...");
543 TPushParser::gdelete_prev_script()
545 logger.debug("gdelete_prev_script: start...");
546 // this method delete a sp or an sb preceding the cursor
547 assert(cursor.prev());
548 assert(cursor.parent());
549 TNode prev = cursor.prev();
550 assert(prev.is("sp") || prev.is("sb"));
553 // i can invoke the gdelet_prev, because a sp (sb) MUST have two child
555 logger.debug("gdelete_prev_script: bye...");
559 TPushParser::gdelete_prev_group()
561 logger.debug("gdelete_prev_group: start");
562 assert(cursor.prev() && cursor.prev().isG());
563 TNode prev = cursor.prev();
566 logger.debug("gdelete_prev_group: i have to call the do_gdelete()");
568 // a group can have no child, so the gdelete_prev is not appropriate
569 // so this method is not equivalent to the one above
572 logger.debug("gdelete_prev_group: do_gdelete terminated");
573 logger.debug("gdelete_prev_group: bye...");
577 TPushParser::gdelete_prev_macro()
579 logger.debug("gdelete_prev_macro: start");
580 assert(cursor.parent());
581 assert(cursor.prev());
582 TNode prev = cursor.prev();
585 const TDictionary::Entry& entry = dictionary.find(prev["name"]);
587 if (!entry.defined())
589 // I assume tha the user want to completely delete the undefined macro
590 logger.debug("gdelete_prev_macro: i have to remove an undefined macro");
592 prev.replace(cursor);
596 logger.debug("gdelete_prev_macro: i have to start to delete a defined MACRO");
598 // i start to remove a MACRO. Different actions must be taken, based on the nature
599 // of the MACRO. In some case, we can't remove the MACRO immediately, in other
600 // case it's correct. In the first case, we have to update the stack, pushing
601 // a frame in the stack with a correct value of pos, in the
602 // second one, we do not have to push a frame in the stack
606 // if the control element is ightOpen, the cursor should be placed after
607 // the last child of the control element's last child, and try to remove something
608 // A frame MUST be pushed in the stack, because we dont' know if the following actions
609 // will remove the MACRO.
610 logger.debug("gdelte_prev_macro: i have to delete a control rightOpen, so i push an element in the stack");
612 frames.push(Frame(entry));
614 logger.debug("gdelete_prev_macro: i control the values of the pushed frame");
615 logger.debug("gdelete_prev_macro: rightOpen");
616 cout << frames.top().entry.rightOpen << endl;
617 logger.debug("gdelete_prev_macro: leftOpen");
618 cout << frames.top().entry.leftOpen << endl;
619 logger.debug("gdelete_prev_macro: pattern.empty()");
620 cout << frames.top().entry.pattern.empty() << endl;
622 // Since the MACRO is rightOpen, the last child of the MACRO must be a phantom group
623 assert(prev.last().isG() && !prev.last().hasId());
626 prev.last().append(cursor);
628 // the gdelete_prev is not appropriate, because the last child of the MACRO could have no child
629 logger.debug("gdelete_prev_macro: i have to call do_gdelete_phantom_group");
630 do_gdelete_phantom_group();
634 logger.debug("gdelete_prev_macro: after the do_gdelete_phantom_group, the frame in the stack has to be rightOpen");
635 cout << frames.top().entry.rightOpen << endl;
636 logger.debug("gdelte_prev_macro: after the do_gdelete_phantom_group, the top element's pattern MUST be empty");
637 cout << frames.top().entry.pattern.empty() << endl;
640 logger.debug("gdelete_prev_macro: the do_gdelete_phantom_group has removed all frames in the stack");
643 else if (entry.leftOpen)
645 logger.debug("gdelete_prev_macro: i have to delete a control element with leftOpen");
646 // the leftOpen MACRO MUST have one and only one child, which MUST be a phantom group
647 // In this case, we do not have to push a frame in the stack, because we remove the
648 // MACRO immediately, substituting it with the content of the phantom group.
649 // At the moment, i don't remove the last child of the phantom group, but
650 // i don't know if it's the correct behavior of the graphical deleting.
651 // To delete it, just remove the comment of the last instruction
652 assert(prev.first());
653 assert(prev.first().isG());
654 assert(prev.first() == prev.last());
656 TNode g = prev.first();
658 prev.replace(g.first(), TNode());
661 else if (!entry.pattern.empty())
663 // we have to start removing a MACRO which accepts arguments.
664 // a MACRO without child does not exist
666 logger.debug("gdelete_prev_macro: i have to remove a MACRO with argument");
667 assert(prev.size() >= 1);
669 // Differnt actions should be taken, based on the nature of the last child
670 // of the MACRO. We have to distinguish the case in which it's a delimited argument
671 // frome the one in which it's a not delimited argument.
672 if (prev.last().isG() && !prev.last().hasId())
674 // the last argument of the MACRO is a delimited argumet. We ideally remove
675 // the sequence of delimiters
676 logger.debug("gdelete_prev_macro: the last argument of the MACRO is delimited");
678 prev.last().append(cursor);
679 // i have to push a frame with a correct value of pos
680 assert(entry.previousParam(entry.pattern.size()) != entry.pattern.size());
681 unsigned sequence_length = entry.pattern.size() - entry.previousParam(entry.pattern.size()) - 1;
682 unsigned p = entry.pattern.size() - sequence_length - 1;
683 frames.push(Frame(entry, p));
684 logger.debug("gdelete_prev_macro: i have inserted a frame, it's pos is: ");
685 cout << frames.top().pos << endl;
690 // in this case, the last child of the MACRO is not a delimited argument, so we try
691 // to remove it, but we have to take differnt actions if the MACRO is a table with rows or not.
692 logger.debug("gdelete_prev_macro: the last argumet of the MACRO is not delimited, so itry to remove it");
694 if (entry.table == 1 && prev.last().is("row"))
696 // in this case the cursor should be appended to the group associated to
697 // the last cell of the last row of the table
698 logger.debug("gdelete_prev_macro: but it is a table with rows, so i append the cursor at the end of the table");
699 assert(prev.last().last().is("cell") && prev.last().last().first().isG());
700 prev.last().last().first().append(cursor);
704 logger.debug("gdelete_prev_macro: i append the cursor to the MACRO");
708 frames.push(Frame(entry, entry.pattern.size()));
710 logger.debug("gdelete_prev_macro: i've pushed a frame in the stack, and it's value of pos is ");
711 cout << frames.top().pos << endl;
715 logger.debug("gdelete_prev_macro: i invoke the gdelete_prev");
720 logger.debug("gdelete_prev_macro: i invoke the do_gdelete");
723 } // end of the else of the if (prev.last().isG() && !prev.last().hasId())
725 } // end of if (!entry.pattern.empty())
728 // if we are here, the MACRO preceding the cursor, is not rightOpen, nor leftOpen,
729 // and has no pattern. It means that it has no childs.
730 // We can replace it with the cursor
731 logger.debug("gdelete_prev_macro: the MACRO is empty, i remove it");
732 assert(prev.size() == 0);
734 prev.replace(cursor);
736 } // end of defined MACRO
738 logger.debug("gdelete_prev_macro: bye...");
742 TPushParser::gdelete_prev()
744 // if in this function, the prev of cursor does exist, also the parent and we want a graphical deleting.
746 logger.debug("gdelete_prev: start...");
748 assert(cursor.prev());
749 assert(cursor.parent());
751 TNode prev = cursor.prev();
753 if (prev.is("i") || prev.is("o") || prev.is("n"))
755 gdelete_prev_token();
757 else if (prev.isSp() || prev.isSb())
759 gdelete_prev_script();
763 gdelete_prev_group();
767 // here we also treat the case in which the MACRO is a table
768 gdelete_prev_macro();
772 // not handled. Future cases...
775 logger.debug("gdelete_prev: bye...");
779 TPushParser::rgreplace_futher(void)
781 // this function MUST only be invoked, when the cursor
782 // is the only child of a group with id. This function
783 // replace the group with the cursor. But if the new parent
784 // is a group with id and the cursor is the only child of the
785 // group, the new parent is replaced...and so on.
786 // r stands for recursive, g stands for graphical
787 assert(cursor.parent());
788 assert(cursor.parent().isG() && cursor.parent().hasId());
790 TNode parent = cursor.parent();
792 while (parent.isG() && parent.hasId() && (parent.first() == cursor))
794 parent.replace(cursor);
795 parent = cursor.parent();
800 // in this case we have removed a MACRO's child.
801 // I can assert that this MACRO accepts arguments.
802 assert(!frames.empty());
803 Frame& frame = frames.top();
804 assert(frame.pos > 0);
810 TPushParser::do_gdelete_script()
812 // If we are here, the cursor is child of a script and
813 // means that a prev MUST exist and that there is only an
814 // element preceding the cursor. The sp's (or sb's) parent
815 // MUST NOT be a MACRO.
816 // The element preceding the cursor is the base of the script.
818 logger.debug("do_gdelete_script: start...");
820 assert(cursor.parent() && (cursor.parent().isSp() || cursor.parent().isSb()));
821 TNode parent = cursor.parent();
823 assert(parent.size() == 2);
824 assert(parent.parent() && !parent.parent().isC());
826 TNode prev = cursor.prev();
828 if (prev.isG() /*&& !prev.hasId()*/ && (prev.size() == 0))
830 // in this case, the script's base is a group with no elements, so i think
831 // we have to remove the entire MACRO, replacing it with the cursor
833 parent.replace(cursor);
835 // if the new parent is a group with Id and the cursor is the only
836 // element of this group, we have to remove it. This controls are made
837 // in the method rgreplace_futher().
838 if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_futher();
842 // in this case, the prev has to replace the script,
843 // and the curosor has to be placed after the prev.
844 assert(prev.hasId());
845 parent.replace(prev);
846 prev.parent().append(cursor);
847 // now prev should have a preceding element
848 assert(cursor.parent().size() > 1);
851 logger.debug("do_gdelete_script: bye...");
853 } // end of method do_gdelet_script
856 TPushParser::do_gdelete_macro()
858 // If we are here, the cursor is a child of a MACRO and this means
859 // that there is an open frame for the control element
860 // and this element is closed at either side (no leftOpen no rightOpen)
861 // and the MACRO was waiting for a non delimited argument, so
862 // we can assert that frame.entry.pattern.size() >= 1
863 assert(cursor.parent() && cursor.parent().isC());
864 TNode parent = cursor.parent();
866 assert(!frames.empty());
867 Frame& frame = frames.top();
868 assert(frame.entry.pattern.size() >= 1);
870 // we have to take different actions, based on if a preceding element exists
872 logger.debug("do_gdelete_macro:");
873 TNode prev = cursor.prev();
876 // in this case, a prev does not exist, so the actions of deleting means
877 // that we have to remove the MACRO. So we have to pop the stack.
878 // Being here also means that the MACRO is waiting for the first argument
879 // (which is not delimited)
880 assert(frame.pos == 0);
881 parent.replace(cursor);
884 // if the new parent is a group with Id, and has no elements other than the
885 // cursor, we could remove it...but i'm not sure
886 if (cursor.parent() && cursor.parent().isG() && cursor.parent().hasId())
888 else if (cursor.parent().isC())
890 // We have assumed that a MACRO cannot be a MACRO's child.
891 // At the moment, this assumption is valid, but in a future
892 // it could be false.
893 assert(!frames.empty());
894 Frame& frame = frames.top();
895 assert(frame.pos > 0);
901 // we have to control if prev is a delimited argument or not.
902 if (prev.isG() && !prev.hasId())
904 // in this case, prev is a delimited argument, so we have
905 // to ideally remove the sequence of delimiters
906 Frame& frame = frames.top();
907 assert(frame.pos > 1);
908 logger.debug("do_gdelete_macro: the pos of the frame is: ");
909 cout << frame.pos << endl;
910 logger.debug("do_gdelete_macro: the pattern size of the entry is");
911 cout << frame.entry.pattern.size() << endl;
912 logger.debug("do_gdelete_macro: the prev param is at");
913 cout << frame.entry.previousParam(frame.pos) << endl;
916 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
917 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
918 assert(sequence_length);
919 frame.pos = frame.pos - sequence_length - 1;
923 // the prev is not a delimited argument, so we have to try to remove it.
924 // I told "try" because the prev can be a group or something that
925 // a simple delete cannot remove completely
930 logger.debug("do_gdelete_macro: bye...");
934 TPushParser::do_gdelete_groupId()
936 // if we are here, the cursor's parent is a group with Id
937 assert(cursor.parent() && cursor.parent().isG() && cursor.parent().hasId());
938 TNode parent = cursor.parent();
940 // we have to take different actions based on if the cursor has a preceding
942 TNode prev = cursor.prev();
945 // the cursor has a preceding element, so we try to remoev it
948 // We control if the group has to be removed, because the cursor
949 // could be the only element of the group.
950 if ((parent.first() == cursor) && parent.isG() && parent.hasId())
956 // the cursor has no preceding elements, so we have to remove the
960 // i have to re-start the process, because it' a graphical delete
964 logger.debug("do_gdelete_groupId: bye...");
966 } // end of method do_gdelete_groupId()
969 TPushParser::do_gdelete_phantom_group()
971 // if we are here, the cursor MUST be a child of a
973 logger.debug("do_gdelete_phantom_group: start");
974 assert(cursor.parent() && cursor.parent().isG() && !cursor.parent().hasId());
976 TNode parent = cursor.parent();
978 // now we have to control if the cursor has a preceding element or not
979 TNode prev = cursor.prev();
982 // the cursor has a preceding element, so we try to remove it
983 logger.debug("do_gdelete_phantom_group: i have to call the gdelete_prev()");
985 logger.debug("do_gdelete_phantom_group: gdelete_prev terminated");
987 if (parent.size() == 1 && parent.parent().isSp())
989 logger.debug("do_gdelete_phantom_group: i have to remove a phantom group which is a child of a sp");
990 // in this case the gdelete_prev has removed the only element preceding the cursor.
991 // If the phantom group is a sp's child, it means that the user has removed all \' in the
992 // phantom group. I think, we can remove the phamtom group and the sp element.
994 parent.replace(cursor);
995 // now we have a sp element with two children: the first child (we don't know nothing about it)
997 assert(cursor.parent().size() == 2);
999 // to delete the script we can invoke the do_gdelete_script(), which will do all controls we need
1000 do_gdelete_script();
1002 else if (parent.parent().isSp())
1004 // in this case we have to place the cursor after the sp element
1005 logger.debug("do_gdelete_phantom_group: the sequence of \' is not terminated, so i place the cursor after the sp element");
1007 assert(parent.parent().parent());
1008 parent.parent().parent().append(cursor);
1013 // in this case the cursor is the only element of the phantom group,
1014 // so we have to remove it. But, a phantom group has a special role,
1015 // so we have to control the grand futher of the cursor.
1016 TNode gfuther = parent.parent();
1019 // If here, the TML tree is in an inconsistent state
1020 logger.error("do_gdelete_phantom: TML tree in a inconsistent state");
1022 else if (gfuther.isC())
1024 // in this case the phantom group is child of a MACRO.
1025 // We have to control the nature of this MACRO.
1026 logger.debug("do_gdelete_phantom_group: i have to remove a phantom group which is a child of a MACRO");
1027 assert(!frames.empty());
1028 Frame& frame = frames.top();
1030 if (frame.entry.leftOpen && frame.entry.rightOpen)
1032 // in this case, the cursor is in the second and last child
1033 // of the MACRO. We can assert that the grand futher has two
1034 // children. which are both phantom group
1035 logger.debug("do_gdelete_phantom_group: the MACRO is leftOpen and rigthOpen");
1036 assert(gfuther.size() == 2);
1037 assert((gfuther.last() == parent) && (gfuther.first().isG() && !gfuther.first().hasId()));
1038 assert(frame.pos == 0);
1040 TNode ggfuther = gfuther.parent();
1044 // i have to replace the gfuther with the elements of its first child
1045 gfuther.replace(gfuther.first().first(), TNode());
1046 logger.debug("do_gdelete_phantom_group: i have removed the control element, and replaced it with its first child");
1047 ggfuther.append(cursor);
1048 logger.debug("do_gdelete_phantom_group: cursor appended to the grand grand futher");
1049 // now we have the situation preceding the insertion of the MACRO leftOpen and rightOpen
1050 // this MACRO no longer exists.
1053 else if (frame.entry.rightOpen)
1055 // the user has inserted a MACRO rightOpen. Since the cursor is the
1056 // only child of the MACRO, the user want to remove it.
1057 // We can assert that cursor's parent is the only child of the MACRO
1058 logger.debug("do_gdelete_phantom_group: the MACRO is rightOpen only");
1059 assert(gfuther.size() == 1);
1060 assert(frame.pos == 0);
1063 gfuther.replace(cursor);
1065 // now we have the situation preceding the MACRO rightOpen, so i have to pop the frame
1068 else if (frame.entry.leftOpen)
1070 // i think this situation will never occur
1071 // but it can be recovered in some way
1072 logger.error("the parser has generated a wrong TML tree");
1074 else if (!frame.entry.pattern.empty())
1076 // the MACRO accepts arguments, and the phantom group in which
1077 // the cursor is, rappresents a delimited argument
1078 // We have to control if the cursor's parent has a preceding element,
1080 logger.debug("do_gdelete_phantom_group: the MACRO accepts arguments");
1081 logger.debug("do_gdelete_phantom_group: entry pattern has size:");
1082 cout << frame.entry.pattern.size() << endl;
1083 TNode uncle = parent.prev();
1086 // the parent is the only element of the MACRO.
1087 // we can assert that frame.pos == 0.
1088 // In this case we can replace the MACRO with the cursor
1089 assert(frame.pos == 0);
1092 gfuther.replace(cursor);
1097 // the parent has a preceding element. Now we have
1098 // to control if the uncle is a delimited argument or not.
1099 if (uncle.isG() && !uncle.hasId())
1101 // the uncle is a delimited argument. So we have to ideally
1102 // remove the sequrnce of delimiters.
1103 assert(frame.pos > 1);
1104 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1105 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1106 assert(sequence_length);
1107 // sequence_length is the length of the delimiters sequence which separates
1108 // the current parameter and the previous parameter
1109 frame.pos = frame.pos - sequence_length - 1;
1112 uncle.append(cursor);
1116 // the uncle is a not delimited argument, so we try to remove it.
1118 parent.replace(cursor);
1119 parent = cursor.parent(); // i update the parent (it should be the MACRO)
1120 assert(parent.isC());
1122 // now i try to remove the uncle (now it' the preceding element)
1125 } // this is the else's end, that handles the case in which an uncle exists
1126 } // end of if (!frame.entry.pattern.empty())
1129 // the entry has no arguments, is not rightOpen and is not leftOpen.
1130 logger.warning("do_gdelete_phantom_group: TML tree in a strange state");
1131 if (gfuther.size() == 1)
1135 gfuther.replace(cursor);
1139 logger.debug("do_gdelete_phantom_group: TML tree in a very strange state");
1141 gfuther.replace(cursor);
1144 } // end of if (gfuther.isC())
1145 else if (gfuther.is("cell"))
1147 // we have to handle the case where cursor'grand futher is a cell element.
1148 // The tables are control sequece, so there is a frame in the stack
1149 assert(!frames.empty());
1150 assert(frames.top().pos == 1);
1151 assert(frames.top().entry.table == 1);
1153 // a cell MUST be child of row element, which in turn MUST be child of an element
1154 // havin attribute table.
1155 assert(gfuther.parent() && gfuther.parent().is("row") && gfuther.parent().parent());
1156 TNode row = gfuther.parent();
1158 // in this case the cell has no element, so the user wants to delete this cell.
1159 TNode prev_cell = gfuther.prev();
1163 // now the cell no longer exists
1167 // i this case, the cell is the only cell in the row.
1168 // So, i assume that the user wants to delete the entire row.
1169 TNode table = row.parent();
1170 TNode prev_row = row.prev();
1175 // the row was the only child of the table.
1176 // I think have to delete the entire table
1177 assert(table.parent());
1178 TNode parent_table = table.parent();
1181 parent_table.append(cursor);
1185 // there are other rows (one at least)
1186 assert(prev_row.is("row"));
1187 assert(prev_row.last());
1188 TNode last_cell = prev_row.last();
1189 assert(last_cell.is("cell"));
1190 assert(last_cell.size() == 1);
1191 assert(last_cell.first().isG() && !last_cell.first().hasId());
1192 last_cell.first().append(cursor);
1194 } // end of if (!prev_cell)
1197 // being here means that there is a previous cell,
1198 // so we append the cursor to group.
1199 assert(prev_cell.size() == 1);
1200 assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1201 prev_cell.first().append(cursor);
1203 } // end of if (gfuther.is("cell"))
1204 else if (gfuther.isSp())
1206 // in this case, the user pushed a \'. So this phantom group
1207 // contained a sequence of \'.
1208 // Maybe in this part will never be used, because, if we delete last \' in the
1209 // phantom group, we remove the phantom group also
1211 // In any case, if we are here we have two choice:
1212 // delete the phantom group;
1213 // delete the superscript.
1217 // cursor' grand futher is undefined
1218 logger.error("do_gdelete_phantom_group: TML tree in a unknown state");
1220 } // end of the else of the if (prev)
1222 if (!frames.empty())
1224 logger.debug("do_gdelete_phantom_group: the stack is not empty");
1225 logger.debug("do_gdelte_phanto_group: is the top element rightOpen?");
1226 cout << frames.top().entry.rightOpen << endl;
1227 logger.debug("do_gdelte_phanto_group: is the top element'pattern empty?");
1228 cout << frames.top().entry.pattern.empty() << endl;
1230 logger.debug("do_gdelete_phantom_group: bye...");
1235 TPushParser::do_gdelete()
1237 logger.debug("do_gdelete: start");
1238 // we have to handle the case in wich the cursor has a parent or not
1239 if (!cursor.parent())
1241 // it's not a good situation...at the moment we do not take actions
1242 logger.error("TML tree not well structured");
1246 // a parent exists. We have to take differnt actions, based on the nature of
1248 TNode parent = cursor.parent();
1249 if (parent.is("math"))
1251 // we ca do two thing...remove the math mode (it implies controlling the display attribute), do nothing
1252 // At the moment, the user's will of deleting is simply ignored
1253 logger.error("TML tree not well structured");
1255 else if (parent.isG())
1257 // the cursor's parent is a group. We have to control if it's a phantom group or not
1258 logger.debug("do_gdelete: the cursor's parent is a group");
1261 logger.debug("do_gdelete: which has Id");
1262 do_gdelete_groupId();
1266 logger.debug("do_gdelete: which is a phantom group");
1267 do_gdelete_phantom_group();
1269 } // end of parent is group
1270 else if (parent.isC())
1272 cout << "the cursor's parent is a MACRO" << endl;
1274 } // end of parent is a MACRO
1275 else if (parent.isSp() || parent.isSb())
1277 cout << "the cursor's parent is a script" << endl;
1278 do_gdelete_script();
1279 } // end of parent is sp or sb
1280 } // end of the else which consider the case in which parent exists
1282 if (!cursor.parent())
1283 logger.debug("do_gdelete: the cursro has no parent -> paolo is stupid");
1285 if (!frames.empty())
1287 logger.debug("do_gdelete: the stack is not empty");
1288 logger.debug("do_gdelete: is the top element rightOpen?");
1289 cout << frames.top().entry.rightOpen << endl;
1290 logger.debug("do_gdelete: is the top element'pattern empty");
1291 cout << frames.top().entry.pattern.empty() << endl;
1293 logger.debug("do_gdelete: bye...");
1295 } // end of method do_gdelete
1299 TPushParser::do_gdelete()
1301 // this function operates a graphical deleting
1303 //if (!frames.empty())
1304 // cout << "do_gdelete: c'e' un frame aperto e il suo pos vale: " << frames.top().pos << endl;
1306 TNode parent = cursor.parent();
1308 // if no parent, do nothing
1314 TNode prev = cursor.prev();
1317 // i try to delete the preceding element
1320 if ((parent.first() == cursor) && parent.isG() && parent.hasId())
1324 else // no previous node is present
1326 // if here, we are in a gruop whose only child is the cursor.
1328 if (!parent.hasId())
1330 // the parent is a phantom group
1331 assert(parent.parent());
1332 if (!parent.parent().is("math"))
1334 TNode gfuther = parent.parent();
1336 // if the grand futher is a group with Id, it should be removed,
1337 // but i don't know if it will never occur...
1338 if (gfuther.isG() && gfuther.hasId())
1341 parent.replace(cursor);
1343 // re-start the process
1346 else if (gfuther.isC())
1348 // the grand futher is a control element: since the parent is a phantom group,
1349 // the TML tree should be in a inconsistent state (once removed the parent).
1351 // being here means that there is a frame in the stack
1352 assert(!frames.empty());
1353 cout << "do_gdelete: i have to remove a phantom group which is a child of a MACRO" << endl;
1354 Frame& frame = frames.top();
1355 cout << frame.entry.leftOpen << frame.entry.rightOpen << endl;
1356 if (frame.entry.leftOpen && frame.entry.rightOpen)
1358 // in this case, the cursor is in the second and last child
1359 // of the MACRO. We can assert that the grand futher has two
1360 // children. which are both phantom group
1361 cout << "do_gdelete: the MACRO is leftOpen and rigthOpen" << endl;
1362 assert(gfuther.size() == 2);
1363 assert((gfuther.last() == parent) && (gfuther.first().isG() && !gfuther.first().hasId()));
1364 assert(frame.pos == 0);
1366 TNode ggfuther = gfuther.parent();
1370 // i have to replace the gfuther with the elements of its first child
1371 gfuther.replace(gfuther.first().first(), TNode());
1372 cout << "do_gdelete: i have removed the control element, and replaced it with its first child" << endl;
1373 ggfuther.append(cursor);
1374 cout << "do_gdelete: cursor appended to the grand grand futher" << endl;
1376 // now we have the situation preceding the insertion of the MACRO leftOpen and rightOpen
1377 // this MACRO no longer exists.
1380 else if (frame.entry.rightOpen)
1382 // the user has inserted a MACRO rightOpen. Since the cursor is the
1383 // only child of the MACRO, the user want to remove it.
1384 // We can assert that cursor's parent is the only child of the MACRO
1385 cout << "do_gdelete: the MACRO is rightOpen only" << endl;
1386 assert(gfuther.size() == 1);
1387 assert(frame.pos == 0); // i think this assert has no sense
1391 gfuther.replace(cursor);
1393 // now we have the situation preceding the MACRO rightOpen, so i have to pop the frame
1396 else if (frame.entry.leftOpen)
1398 // it' s an unpredicted situation
1399 cout << "it's a bad situation, maybe handlable, but unpredicted" << endl;
1401 else if (!frame.entry.pattern.empty())
1403 // the MACRO (the cursor's grand futher) accepts arguments.
1404 // we have to control if the cursor's uncle does exist.
1408 // in this case, we can assert that frame in the stack has
1409 // pos greater than 0
1410 cout << "this is the assert that fails" << endl;
1411 assert(frame.pos > 0);
1413 // cursor's uncle does exist. we have to control
1414 // its nature (is it a phantom group?)
1415 TNode uncle = parent.prev();
1416 if (uncle.isG() && !uncle.hasId())
1418 // the cursor's uncle is a phantom group, so it was a
1419 // delimited argument of the MACRO and the corrisponding sequence of
1420 // delimeters is inserted. So, the action of deleting means
1421 // removing this sequence
1422 assert(frame.pos > 1);
1423 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1424 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1425 assert(sequence_length);
1426 // sequence_length is the length of the delimiters sequence which separates
1427 // the current parameter and the previous parameter
1428 frame.pos = frame.pos - sequence_length - 1;
1431 uncle.append(cursor);
1435 // the uncle was a NOT delimited argument. So i try to
1438 parent.replace(cursor);
1440 parent = cursor.parent(); // i update the parent (it should be the MACRO)
1441 assert(parent.isC());
1449 // cursor's parent is the only child of the MACRO, which accepts arguments
1450 // i can assert that frame.pos == 0.
1451 // In this case i can replace the MACRO with the cursor
1452 assert(frame.pos == 0);
1456 gfuther.replace(cursor);
1463 else if (gfuther.is("cell"))
1465 // being here means that there is a frame in the stack
1466 // associated to the "table"
1467 cout << "do_gdelete: i have to delete a cell" << endl;
1468 assert(!frames.empty());
1469 assert(frames.top().pos == 1);
1470 assert(frames.top().entry.table == 1);
1471 // a cell MUST be child of row element, which in turn MUST be child of an element
1472 // havin attribute table.
1473 assert(gfuther.parent() && gfuther.parent().is("row") && gfuther.parent().parent());
1474 TNode row = gfuther.parent();
1476 // in this case the cell has no element, so the user wants to delete this cell.
1477 TNode prev_cell = gfuther.prev();
1482 // now the cell no longer exists
1486 // i this case, the cell is the only cell in the row.
1487 // So, i assume that the user wants to delete the entire row.
1488 TNode table = row.parent();
1489 TNode prev_row = row.prev();
1493 // the row was the only child of the table.
1494 // I think have to delete the entire table
1495 assert(table.parent());
1496 TNode parent_table = table.parent();
1499 parent_table.append(cursor);
1503 // there are other rows (one at least)
1504 assert(prev_row.is("row"));
1505 assert(prev_row.last());
1506 TNode last_cell = prev_row.last();
1507 assert(last_cell.is("cell"));
1508 assert(last_cell.size() == 1);
1509 assert(last_cell.first().isG() && !last_cell.first().hasId());
1510 last_cell.first().append(cursor);
1515 // being here means that there is a previous cell,
1516 // so we append the cursor to group.
1517 assert(prev_cell.size() == 1);
1518 assert(prev_cell.first().isG() && !prev_cell.first().hasId());
1519 prev_cell.first().append(cursor);
1523 else // the grand futher is math
1525 // nothing to do...i think
1526 assert(frames.empty());
1531 // the parent is a group with id and has no elements other than cursor
1532 // so we replace it with the cursor.
1535 // i have to re-start the process, because it' a graphical delete
1540 else if (parent.isC())
1542 // being here means that there is an open frame for the control element
1543 // and this element is closed at either side (no leftOpen no rightOpen)
1544 // and the MACRO was waiting for a non delimited argument, so
1545 // we can assert that frame.entry.pattern.size() >= 1
1546 assert(!frames.empty());
1547 Frame& frame = frames.top();
1549 assert(frame.entry.pattern.size() >= 1);
1551 cout << "do_gdelete: frames.top().pos = " << frames.top().pos << endl;
1553 TNode prev = cursor.prev();
1557 // in this case we can replace the MACRO with the cursor
1558 // and pop the stack and we can assert that frame.pos == 0
1559 assert(frame.pos == 0);
1560 cursor.remove(); // it should not be necessary, but i'm not shure
1561 parent.replace(cursor);
1566 // in this case the cursor has a preceding element
1567 // and there are differnt things based on the nature
1568 // of the prev: if it's a phantom group do something,
1569 // else do something else
1570 if (prev.isG() && !prev.hasId())
1572 // in this case we have to append the cursor
1573 // to the prev and decrement frame.pos of the length of
1574 // delimiters sequence that delimitates the preceding argument.
1575 // So we ideally remove this sequence
1576 assert(frame.pos > 1);
1578 prev.append(cursor);
1579 assert(frame.entry.previousParam(frame.pos) != frame.entry.pattern.size());
1580 unsigned sequence_length = frame.pos - frame.entry.previousParam(frame.pos) - 1;
1581 assert(sequence_length);
1582 frame.pos = frame.pos - sequence_length - 1;
1586 // the prev is an non delimited argument, so we try to
1592 else if (parent.is("sp") || parent.is("sb"))
1594 // being here means that a prev MUST exist
1595 // and that there is only an element preceding the cursor.
1596 // The sp's (or sb's) parent MUST NOT be a MACRO
1598 assert(parent.size() == 2);
1599 assert(parent.parent() && !parent.parent().isC());
1601 TNode prev = cursor.prev();
1603 if (prev.isG()*/ /*&& !prev.hasId()*/ /* && (prev.size() == 0))
1606 parent.replace(cursor);
1608 // now, cursor should be the only parent's child
1609 assert(cursor.parent().size() == 1);
1611 if (cursor.parent().isG() && cursor.parent().hasId()) rgreplace_futher();
1615 assert(prev.hasId());
1616 parent.replace(prev);
1617 prev.parent().append(cursor);
1618 // now prev should have a preceding element
1619 assert(cursor.parent().size() > 1);
1624 // not handled: no control for tables, ...
1629 // the cursor has no parent!!!
1631 // emit an error? and if we want to emit an error, in which way?
1638 TPushParser::process(const TToken& token)
1640 switch (token.category)
1642 case TToken::BEGIN: do_begin(); break;
1643 case TToken::END: do_end(); break;
1644 case TToken::SHIFT: do_shift(); break;
1645 case TToken::ALIGN: do_align(); break;
1646 case TToken::EOL: do_eol(); break;
1647 case TToken::PARAMETER: do_parameter(token.value); break;
1648 case TToken::SUPERSCRIPT: do_superscript(); break;
1649 case TToken::SUBSCRIPT: do_subscript(); break;
1650 case TToken::SPACE: do_space(token.value); break;
1651 case TToken::LETTER: do_letter(token.value); break;
1652 case TToken::DIGIT: do_digit(token.value); break;
1653 case TToken::OTHER: do_other(token.value); break;
1654 case TToken::ACTIVE: do_active(token.value); break;
1655 case TToken::COMMENT: do_comment(); break;
1656 case TToken::CONTROL: do_control(token.value); break;
1657 case TToken::GDELETE: do_gdelete(); break;
1662 TPushParser::push(const TToken& token)
1664 logger.debug("TPushParser::push " + token.value + " (cat: ");
1665 cout << token.category << ")" << endl;
1667 if (token.category == TToken::GDELETE)
1669 logger.debug("push: i have to process a token with category member = GDELETE");
1675 logger.debug("push: i get the cursor's parent");
1676 TNode parent = cursor.parent();
1677 // If the cursor has no parent then it is detached from the editing
1678 // tree, which means this token will be ignored
1681 // If the parent is a phantom group and the grand-parent is a
1682 // control sequence, there are two cases:
1683 // a. we are parsing a delimited argument of a entry
1684 // b. we are parsing a side of a right- or left-open entry
1685 if (parent.isG() && !parent.hasId() && parent.parent().isC())
1687 // There must be an open frame, for the grand-parent is a control sequence
1688 logger.debug("push: cursor'parent is a phantom group, and this group has a MACRO as parent");
1689 logger.debug("push: i assert that !frames.empty()");
1690 assert(!frames.empty());
1691 logger.debug("push: this assert is OK");
1692 logger.debug("push: now i take the top element of the stack");
1693 Frame& frame = frames.top();
1694 logger.debug("push: now i've got the top element");
1695 if (!frame.entry.pattern.empty())
1697 // The entry pattern is not empty. By our conventions this means
1698 // the entry cannot be open at either end, hence we are parsing
1699 // a delimited argument
1700 logger.debug("push: the frame.entry accepts arguments");
1701 assert(frame.pos + 1 < frame.entry.pattern.size());
1702 assert(frame.entry.pattern[frame.pos + 1].category != TToken::PARAMETER);
1703 if (frame.entry.pattern[frame.pos + 1] == token)
1705 // The token matches with a delimiter of the argument,
1706 // so we increment the frame.pos
1709 if (frame.entry.lastDelimiter(frame.pos))
1711 // this delimiter is the last one for the argumet,
1712 // so the argument is completed
1716 if (frame.pos == frame.entry.pattern.size())
1718 // This token has completed the entry
1720 advance(parent.parent());
1722 else if (frame.entry.paramDelimited(frame.pos))
1724 // For the next is a delimited argument we have to place
1725 // a suitable phantom group with the cursor inside
1726 TNode g = doc.createG();
1727 parent.parent().append(g);
1731 parent.parent().append(cursor);
1736 // Delimiter mismatch.
1737 if (frame.entry.pattern[frame.pos].category != TToken::PARAMETER)
1739 // in this case, there is a sequence of delimiters that delimitates
1740 // the argument, and the user correctly inserted a portion of this
1741 // sequence, but has inserted a wrong delimiter.
1742 // Here, there are some possibilities:
1743 // - ignore the token, and wait for the correct delimiter
1744 // - ignore the token, wait for the correct delimiter and emit an error
1745 // If we want to emit an error, we shlould implement a class, that handle
1747 // At the moment, the error is printed to screen
1748 cout << "push: it's not the correct delimiter...you have to type " << frame.entry.pattern[frame.pos + 1].value << endl;
1752 // in this case, the sequence of delimiters is composed of one
1753 // delimiter. It means that we have to process the token
1760 // The entry pattern is empty, hence we are parsing a right-open
1761 // entry. What happens if we actually are in the left side?
1762 // This could happen only when re-editing an entered expression
1764 logger.debug("push: the frame.entry has no arguments, so i assert that i's rightOpne");
1765 assert(frame.entry.rightOpen);
1766 logger.debug("push: this assert is OK, now i process the token");
1768 logger.debug("push: the token has been processed");
1771 else if (parent.isC())
1773 // We are parsing a non-delimited argument entry
1775 Frame& frame = frames.top();
1776 assert(frame.pos < frame.entry.pattern.size());
1778 cout << "push: there is a frame with pos " << frame.pos << endl;
1779 if (frame.entry.pattern[frame.pos].category == TToken::PARAMETER)
1781 // As by the TeX parsing rules of undelimited parameters,
1782 // empty spaces are ignored
1783 if (token.category != TToken::SPACE)
1785 // We need to increase the frame position here, becase inside
1786 // process the function advance will be called. At that point
1787 // it will be important for the parser to know that the entry
1788 // has been completed in order to place the cursor correctly
1789 // in the next position
1790 logger.debug("TPushParser.push: we should be here");
1795 else if (frame.entry.pattern[frame.pos] == token)
1797 // The token has been accepted
1799 if (frame.pos < frame.entry.pattern.size() &&
1800 frame.entry.paramDelimited(frame.pos))
1802 // If the next is a delimited argument we have to place
1803 // the phantom group with the cursor inside
1804 TNode g = doc.createG();
1813 // There is a mismatch. Emit an error and ignore the token?
1814 cerr << "ERROR: token ignored: " << token.value << " (cat: " << token.category << ")" << endl;
1822 cout << "ignored token" << endl;
1825 } // this end corresponds to the else of the if (token.category == TToken::GDELETE)
1827 if (factory) factory->documentModified(doc);
1829 if (frames.empty()) cout << "stack vuoto" << endl;
1832 logger.debug("push: the stack is not empty");
1833 logger.debug("push: is the top element rightOpen?");
1834 cout << frames.top().entry.rightOpen << endl;
1835 logger.debug("push: is the pattern empty");
1836 cout << frames.top().entry.pattern.empty() << endl;
1841 TPushParser::advance(const TNode& node)
1844 TNode parent = node.parent();
1846 ; // nothing to do, the cursor is not in the document any more
1847 else if (parent.isG())
1849 TNode next = node.next();
1850 if (next) next.insert(cursor);
1851 else parent.append(cursor);
1853 else if (parent.isC())
1859 Frame& frame = frames.top();
1860 if (frame.pos == frame.entry.pattern.size())
1866 parent.append(cursor);
1869 else if (parent.is("math"))
1876 TPushParser::setCursorHint(const std::string& c)
1879 if (factory) factory->documentModified(doc);