]> git.lyx.org Git - lyx.git/blob - src/mathed/math_cursor.C
d43f118cb0d26550d4b77103a487378e7b7cb4f3
[lyx.git] / src / mathed / math_cursor.C
1 /*
2  *  File:        math_cursor.C
3  *  Purpose:     Interaction for mathed
4  *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5  *  Created:     January 1996
6  *  Description: Math interaction for a WYSIWYG math editor.
7  *
8  *  Dependencies: Xlib, XForms
9  *
10  *  Copyright: 1996, Alejandro Aguilar Sierra
11  *
12  *   Version: 0.8beta, Math & Lyx project.
13  *
14  *   You are free to use and modify this code under the terms of
15  *   the GNU General Public Licence version 2 or later.
16  */
17
18 #ifdef __GNUG__
19 #pragma implementation
20 #endif
21
22 #include <config.h>
23 #include <algorithm>
24 #include <cctype>
25
26 #include "support/lstrings.h"
27 #include "support/LAssert.h"
28 #include "debug.h"
29 #include "LColor.h"
30 #include "Painter.h"
31 #include "math_support.h"
32 #include "formulabase.h"
33 #include "math_cursor.h"
34 #include "math_factory.h"
35 #include "math_arrayinset.h"
36 #include "math_braceinset.h"
37 #include "math_charinset.h"
38 #include "math_deliminset.h"
39 #include "math_hullinset.h"
40 #include "math_scriptinset.h"
41 #include "math_spaceinset.h"
42 #include "math_specialcharinset.h"
43 #include "math_mathmlstream.h"
44
45 #define FILEDEBUG 0
46
47 using std::endl;
48 using std::min;
49 using std::max;
50 using std::swap;
51 using std::isalnum;
52
53 namespace {
54
55 struct Selection
56 {
57         void grab(MathCursor const & cursor)
58         {
59                 data_.clear();
60                 MathCursorPos i1;
61                 MathCursorPos i2;
62                 cursor.getSelection(i1, i2); 
63                 if (i1.idx_ == i2.idx_)
64                         data_.push_back(MathArray(i1.cell(), i1.pos_, i2.pos_));
65                 else {
66                         std::vector<MathInset::idx_type> indices =
67                                 (*i1.par_)->idxBetween(i1.idx_, i2.idx_);
68                         for (MathInset::idx_type i = 0; i < indices.size(); ++i)
69                                 data_.push_back(i1.cell(indices[i]));
70                 }
71         }
72
73         void erase(MathCursor & cursor)
74         {
75                 MathCursorPos i1;
76                 MathCursorPos i2;
77                 cursor.getSelection(i1, i2); 
78                 if (i1.idx_ == i2.idx_)
79                         i1.cell().erase(i1.pos_, i2.pos_);
80                 else {
81                         std::vector<MathInset::idx_type> indices =
82                                 (*i1.par_)->idxBetween(i1.idx_, i2.idx_);
83                         for (unsigned i = 0; i < indices.size(); ++i)
84                                 i1.cell(indices[i]).erase();
85                 }
86                 cursor.cursor() = i1;
87         }
88
89         void paste(MathCursor & cursor) const
90         {
91                 MathArray ar = glue();
92                 cursor.paste(ar);
93         }
94
95         // glues selection to one cell
96         MathArray glue() const
97         {
98                 MathArray ar;
99                 for (unsigned i = 0; i < data_.size(); ++i)
100                         ar.push_back(data_[i]);
101                 return ar;
102         }
103
104         void clear()
105         {
106                 data_.clear();
107         }
108
109         std::vector<MathArray> data_;
110 };
111
112
113 Selection theSelection;
114
115
116 #if FILEDEBUG
117 std::ostream & operator<<(std::ostream & os, MathCursorPos const & p)
118 {
119         os << "(par: " << p.par_ << " idx: " << p.idx_ << " pos: " << p.pos_ << ")";
120         return os;
121 }
122 #endif
123
124 }
125
126
127 MathCursor::MathCursor(InsetFormulaBase * formula, bool left)
128         : formula_(formula), lastcode_(LM_TC_VAR), selection_(false)
129 {
130         left ? first() : last();
131 }
132
133
134 void MathCursor::push(MathAtom & t)
135 {
136         MathCursorPos p;
137         p.par_ = &t;
138         p.idx_ = 0;
139         p.pos_ = 0;
140         Cursor_.push_back(p);
141 }
142
143
144 void MathCursor::pushLeft(MathAtom & t)
145 {
146         //cerr << "Entering atom "; t->write(cerr, false); cerr << " left\n";
147         push(t);
148         t->idxFirst(idx(), pos());
149 }
150
151
152 void MathCursor::pushRight(MathAtom & t)
153 {
154         //cerr << "Entering atom "; t->write(cerr, false); cerr << " right\n";
155         posLeft();
156         push(t);
157         t->idxLast(idx(), pos());
158 }
159
160
161 bool MathCursor::popLeft()
162 {
163         //cerr << "Leaving atom "; par()->write(cerr, false); cerr << " left\n";
164         if (Cursor_.size() <= 1)
165                 return false;
166         //if (nextInset())
167         //      nextInset()->removeEmptyScripts();
168         Cursor_.pop_back();
169         //if (nextAtom())
170         //      nextAtom()->removeEmptyScripts();
171         return true;
172 }
173
174
175 bool MathCursor::popRight()
176 {
177         //cerr << "Leaving atom "; par()->write(cerr, false); cerr << " right\n";
178         if (Cursor_.size() <= 1)
179                 return false;
180         //if (nextInset())
181         //      nextInset()->removeEmptyScripts();
182         Cursor_.pop_back();
183         //if (nextInset())
184         //      nextInset()->removeEmptyScripts();
185         posRight();
186         return true;
187 }
188
189
190
191 #if FILEDEBUG
192 void MathCursor::dump(char const * what) const
193 {
194         lyxerr << "MC: " << what << "\n";
195         for (unsigned i = 0; i < Cursor_.size(); ++i)
196                 lyxerr << "  i: " << i 
197                         << " Cursor: pos: " << Cursor_[i].pos_
198                         << " idx: " << Cursor_[i].idx_
199                         << " par: " << Cursor_[i].par_ << "\n";
200
201         for (unsigned i = 0; i < Anchor_.size(); ++i)
202                 lyxerr << "  i: " << i 
203                         << " Anchor: pos: " << Anchor_[i].pos_
204                         << " idx: " << Anchor_[i].idx_
205                         << " par: " << Anchor_[i].par_ << "\n";
206
207         lyxerr  << " sel: " << selection_ << "\n";
208 }
209
210
211 void MathCursor::seldump(char const * str) const
212 {
213         //lyxerr << "SEL: " << str << ": '" << theSelection << "'\n";
214         //dump("   Pos");
215
216         lyxerr << "\n\n\n=================vvvvvvvvvvvvv=======================   "
217                 <<  str << "\ntheSelection: " << selection_
218                 << " '" << theSelection.glue() << "'\n";
219         for (unsigned int i = 0; i < Cursor_.size(); ++i) 
220                 lyxerr << Cursor_[i].par_ << "\n'" << Cursor_[i].cell() << "'\n";
221         lyxerr << "\n";
222         for (unsigned int i = 0; i < Anchor_.size(); ++i) 
223                 lyxerr << Anchor_[i].par_ << "\n'" << Anchor_[i].cell() << "'\n";
224         //lyxerr << "\ncursor.pos_: " << pos();
225         //lyxerr << "\nanchor.pos_: " << anchor().pos_;
226         lyxerr << "\n===================^^^^^^^^^^^^=====================\n\n\n";
227 }
228
229 #else
230
231 void MathCursor::seldump(char const *) const {}
232 void MathCursor::dump(char const *) const {}
233
234 #endif
235
236
237 UpdatableInset * MathCursor::asHyperActiveInset() const
238 {
239         return par()->asHyperActiveInset();
240 }
241
242
243 bool MathCursor::isInside(MathInset const * p) const
244 {
245         for (unsigned i = 0; i < Cursor_.size(); ++i) 
246                 if (Cursor_[i].par_->nucleus() == p) 
247                         return true;
248         return false;
249 }
250
251
252 bool MathCursor::openable(MathAtom const & t, bool sel) const
253 {
254         if (t->isHyperActive())
255                 return true;
256
257         if (!t->isActive())
258                 return false;
259
260         if (t->asScriptInset())
261                 return false;
262
263         if (sel) {
264                 // we can't move into anything new during selection
265                 if (Cursor_.size() == Anchor_.size())
266                         return false;
267                 if (&t != Anchor_[Cursor_.size()].par_)
268                         return false;
269         }
270         return true;
271 }
272
273
274 bool MathCursor::positionable(MathAtom const & t, int x, int y) const
275 {
276         if (selection_) {
277                 // we can't move into anything new during selection
278                 if (Cursor_.size() == Anchor_.size())
279                         return 0;
280                 //if (t != Anchor_[Cursor_.size()].par_)
281                 //      return 0;
282         }
283
284         return t->nargs() && t->covers(x, y);
285 }
286
287
288 bool MathCursor::posLeft()
289 {
290         if (pos() == 0)
291                 return false;
292         --pos();
293         return true;
294 }
295
296
297 bool MathCursor::posRight()
298 {
299         if (pos() == size())
300                 return false;
301         ++pos();
302         return true;
303 }
304
305
306 bool MathCursor::left(bool sel)
307 {
308         dump("Left 1");
309         if (inMacroMode()) {
310                 macroModeClose();
311                 lastcode_ = LM_TC_VAR;
312                 return true;
313         }
314         selHandle(sel);
315         lastcode_ = LM_TC_VAR;
316
317         if (hasPrevAtom() && openable(prevAtom(), sel)) {
318                 if (prevAtom()->isHyperActive()) {
319                         lyxerr << "entering hyperactive inset\n";
320                 }
321                 pushRight(prevAtom());
322                 return true;
323         } 
324
325         return posLeft() || idxLeft() || popLeft() || selection_;
326 }
327
328
329 bool MathCursor::right(bool sel)
330 {
331         dump("Right 1");
332         if (inMacroMode()) {
333                 macroModeClose();
334                 lastcode_ = LM_TC_VAR;
335                 return true;
336         }
337         selHandle(sel);
338         lastcode_ = LM_TC_VAR;
339
340         if (hasNextAtom() && openable(nextAtom(), sel)) {
341                 if (nextAtom()->isHyperActive()) {
342                         lyxerr << "entering hyperactive inset\n";
343                         int x, y;
344                         getPos(x, y);
345                         nextAtom()->edit(formula()->view(), x, y, 0);
346                 }
347                 pushLeft(nextAtom());
348                 return true;
349         }
350
351         return posRight() || idxRight() || popRight() || selection_;
352 }
353
354
355 void MathCursor::first()
356 {
357         Cursor_.clear();
358         pushLeft(formula_->par());
359 }
360
361
362 void MathCursor::last()
363 {
364         first();
365         end();
366 }
367
368
369 void MathCursor::setPos(int x, int y)
370 {
371         //dump("setPos 1");
372         //lyxerr << "MathCursor::setPos x: " << x << " y: " << y << "\n";
373
374         macroModeClose();
375         lastcode_ = LM_TC_VAR;
376         first();
377
378         cursor().par_  = &formula_->par();
379
380         while (1) {
381                 idx() = 0;
382                 cursor().pos_ = 0;
383                 //lyxerr << "found idx: " << idx() << " cursor: " << pos()  << "\n";
384                 int distmin = 1 << 30; // large enough
385                 for (unsigned int i = 0; i < par()->nargs(); ++i) {
386                         MathXArray const & ar = par()->xcell(i);
387                         int x1 = x - ar.xo();
388                         int y1 = y - ar.yo();
389                         MathXArray::size_type c  = ar.x2pos(x1);
390                         int xx = abs(x1 - ar.pos2x(c));
391                         int yy = abs(y1);
392                         //lyxerr << "idx: " << i << " xx: " << xx << " yy: " << yy
393                         //      << " c: " << c  << " xo: " << ar.xo() << "\n";
394                         if (yy + xx <= distmin) {
395                                 distmin = yy + xx;
396                                 idx()   = i;
397                                 pos()   = c;
398                         }
399                 }
400                 //lyxerr << "found idx: " << idx() << " cursor: "
401                 //      << pos()  << "\n";
402                 if (hasNextAtom() && positionable(nextAtom(), x, y))
403                         pushLeft(nextAtom());
404                 else if (hasPrevAtom() && positionable(prevAtom(), x, y))
405                         pushRight(prevAtom());
406                 else 
407                         break;
408         }
409         //dump("setPos 2");
410 }
411
412
413 void MathCursor::home(bool sel)
414 {
415         dump("home 1");
416         selHandle(sel);
417         macroModeClose();
418         lastcode_ = LM_TC_VAR;
419         if (!par()->idxHome(idx(), pos())) 
420                 popLeft();
421         dump("home 2");
422 }
423
424
425 void MathCursor::end(bool sel)
426 {
427         dump("end 1");
428         selHandle(sel);
429         macroModeClose();
430         lastcode_ = LM_TC_VAR;
431         if (!par()->idxEnd(idx(), pos()))
432                 popRight();
433         dump("end 2");
434 }
435
436
437 void MathCursor::plainErase()
438 {
439         array().erase(pos());
440 }
441
442
443 void MathCursor::plainInsert(MathAtom const & t)
444 {
445         array().insert(pos(), t);
446         ++pos();
447 }
448
449
450 void MathCursor::insert(char c, MathTextCodes t)
451 {
452         //lyxerr << "inserting '" << c << "'\n";
453         plainInsert(MathAtom(new MathCharInset(c, t)));
454 }
455
456
457 void MathCursor::insert(MathAtom const & t)
458 {
459         macroModeClose();
460
461         if (selection_) {
462                 if (t->nargs())
463                         selCut();
464                 else
465                         selDel();
466         }
467
468         plainInsert(t);
469 }
470
471
472 void MathCursor::niceInsert(MathAtom const & t) 
473 {
474         selCut();
475         insert(t); // inserting invalidates the pointer!
476         MathAtom const & p = prevAtom();
477         if (p->nargs()) {
478                 posLeft();
479                 right();  // do not push for e.g. MathSymbolInset
480                 selPaste();
481         }
482 #ifdef WITH_WARNINGS
483 #warning "redraw disabled"
484 #endif
485         //p->metrics(p->size());
486 }
487
488
489 void MathCursor::insert(MathArray const & ar)
490 {
491         macroModeClose();
492         if (selection_)
493                 selCut();
494
495         array().insert(pos(), ar);
496         pos() += ar.size();
497 }
498
499
500 void MathCursor::paste(MathArray const & ar)
501 {
502         Anchor_ = Cursor_;
503         selection_ = true;
504         array().insert(pos(), ar);
505         pos() += ar.size();
506 }
507
508
509 void MathCursor::backspace()
510 {
511         if (pos() == 0) {
512                 pullArg(false);
513                 return;
514         }       
515
516         if (selection_) {
517                 selDel();
518                 return;
519         }
520
521         MathScriptInset * p = prevAtom()->asScriptInset();
522         if (p) {
523                 p->removeScript(p->hasUp());
524                 // Don't delete if there is anything left 
525                 if (p->hasUp() || p->hasDown())
526                         return;
527         }
528
529         --pos();
530         plainErase();
531 }
532
533
534 void MathCursor::erase()
535 {
536         if (inMacroMode())
537                 return;
538
539         if (selection_) {
540                 selDel();
541                 return;
542         }
543
544         // delete empty cells if necessary
545         if (array().empty()) {
546                 bool popit;
547                 bool removeit;
548                 par()->idxDelete(idx(), popit, removeit);
549                 if (popit && popLeft() && removeit)
550                         plainErase();
551                 return;
552         }
553
554         if (pos() == size())
555                 return;
556
557         MathScriptInset * p = nextAtom()->asScriptInset();
558         if (p) {
559                 p->removeScript(p->hasUp());
560                 // Don't delete if there is anything left 
561                 if (p->hasUp() || p->hasDown())
562                         return;
563         }
564
565         plainErase();
566 }
567
568
569 void MathCursor::delLine()
570 {
571         macroModeClose();
572
573         if (selection_) {
574                 selDel();
575                 return;
576         }
577
578         if (par()->nrows() > 1)
579                 par()->delRow(row());
580
581         if (pos() > size())
582                 pos() = size();
583 }
584
585
586 bool MathCursor::up(bool sel)
587 {
588         dump("up 1");
589         macroModeClose();
590         selHandle(sel);
591
592         if (!selection_) {
593                 // check whether we could move into a superscript 
594                 if (hasPrevAtom()) {
595                         MathAtom & p = prevAtom();
596                         if (p->asScriptInset() && p->asScriptInset()->hasUp()) {
597                                 pushRight(p);
598                                 idx() = 1;
599                                 pos() = size();
600                                 return true;
601                         }
602                 }
603
604                 if (hasNextAtom()) {
605                         MathAtom & n = nextAtom();
606                         if (n->asScriptInset() && n->asScriptInset()->hasUp()) {
607                                 pushLeft(n);
608                                 idx() = 1;
609                                 pos() = 0;
610                                 return true;
611                         }
612                 }
613         }
614
615         return goUp() || selection_;
616 }
617
618
619 bool MathCursor::down(bool sel)
620 {
621         dump("down 1");
622         macroModeClose();
623         selHandle(sel);
624
625         if (!selection_) {
626                 // check whether we could move into a subscript 
627                 if (hasPrevAtom()) {
628                         MathAtom & p = prevAtom();
629                         if (p->asScriptInset() && p->asScriptInset()->hasDown()) {
630                                 pushRight(p);
631                                 idx() = 0;
632                                 pos() = size();
633                                 return true;
634                         }
635                 }
636
637                 if (hasNextAtom()) {
638                         MathAtom & n = nextAtom();
639                         if (n->asScriptInset() && n->asScriptInset()->hasDown()) {
640                                 pushLeft(n);
641                                 idx() = 0;
642                                 pos() = 0;
643                                 return true;
644                         }
645                 }
646         }
647
648         return goDown() || selection_;
649 }
650
651
652 bool MathCursor::toggleLimits()
653 {
654         if (!hasPrevAtom())
655                 return false;
656         MathScriptInset * t = prevAtom()->asScriptInset();
657         if (!t)
658                 return false;
659         int old = t->limits();
660         t->limits(old < 0 ? 1 : -1);
661         return old != t->limits();
662 }
663
664
665 void MathCursor::macroModeClose()
666 {
667         string s = macroName();
668         if (s.size()) {
669                 size_type old = pos();
670                 pos() -= s.size();
671                 array().erase(pos(), old);
672                 interpret(s);
673         }
674 }
675
676
677 int MathCursor::macroNamePos() const
678 {
679         for (int i = pos() - 1; i >= 0; --i) { 
680                 MathAtom & p = array().at(i);
681                 if (p->code() == LM_TC_TEX && p->getChar() == '\\')
682                         return i;
683         }
684         return -1;
685 }
686
687
688 string MathCursor::macroName() const
689 {
690         string s;
691         for (int i = macroNamePos(); i >= 0 && i < int(pos()); ++i) 
692                 s += array().at(i)->getChar();
693         return s;
694 }
695
696
697 void MathCursor::selCopy()
698 {
699         seldump("selCopy");
700         if (selection_) {
701                 theSelection.grab(*this);
702                 selClear();
703         }
704 }
705
706
707 void MathCursor::selCut()
708 {
709         seldump("selCut");
710         if (selection_) {
711                 theSelection.grab(*this);
712                 theSelection.erase(*this);
713                 selClear();
714         } else {
715                 theSelection.clear();
716         }
717 }
718
719
720 void MathCursor::selDel()
721 {
722         seldump("selDel");
723         if (selection_) {
724                 theSelection.erase(*this);
725                 if (pos() > size())
726                         pos() = size();
727                 selClear();
728         }
729 }
730
731
732 void MathCursor::selPaste()
733 {
734         seldump("selPaste");
735         theSelection.paste(*this);
736         theSelection.grab(*this);
737         //selClear();
738 }
739
740
741 void MathCursor::selHandle(bool sel)
742 {
743         if (sel == selection_)
744                 return;
745
746         theSelection.clear();
747         Anchor_    = Cursor_;
748         selection_ = sel;
749 }
750
751
752 void MathCursor::selStart()
753 {
754         seldump("selStart");
755         if (selection_)
756                 return;
757
758         theSelection.clear();
759         Anchor_ = Cursor_;
760         selection_ = true;
761 }
762
763
764 void MathCursor::selClear()
765 {
766         seldump("selClear");
767         selection_ = false;
768 }
769
770
771 void MathCursor::selGet(MathArray & ar)
772 {
773         seldump("selGet");
774         if (!selection_)
775                 return;
776
777         theSelection.grab(*this);
778         ar = theSelection.glue();
779 }
780
781
782
783 void MathCursor::drawSelection(Painter & pain) const
784 {
785         if (!selection_)
786                 return;
787
788         MathCursorPos i1;
789         MathCursorPos i2;
790         getSelection(i1, i2);
791         //lyxerr << "selection from: " << i1 << " to " << i2 << "\n";
792
793         if (i1.idx_ == i2.idx_) {
794                 MathXArray & c = i1.xcell();
795                 int x1 = c.xo() + c.pos2x(i1.pos_);
796                 int y1 = c.yo() - c.ascent();
797                 int x2 = c.xo() + c.pos2x(i2.pos_);
798                 int y2 = c.yo() + c.descent();
799                 pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
800         } else {
801                 std::vector<MathInset::idx_type> indices
802                         = (*i1.par_)->idxBetween(i1.idx_, i2.idx_);
803                 for (unsigned i = 0; i < indices.size(); ++i) {
804                         MathXArray & c = i1.xcell(indices[i]);
805                         int x1 = c.xo();
806                         int y1 = c.yo() - c.ascent();
807                         int x2 = c.xo() + c.width();
808                         int y2 = c.yo() + c.descent();
809                         pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
810                 }
811         }
812
813 #if 0
814         // draw anchor if different from selection boundary
815         MathCursorPos anc = Anchor_.back();
816         if (anc != i1 && anc != i2) {
817                 MathXArray & c = anc.xcell();
818                 int x  = c.xo() + c.pos2x(anc.pos_);
819                 int y1 = c.yo() - c.ascent();
820                 int y2 = c.yo() + c.descent();
821                 pain.line(x, y1, x, y2, LColor::mathline);
822         }
823 #endif
824 }
825
826
827 void MathCursor::handleFont(MathTextCodes t)
828 {
829         macroModeClose();
830         if (selection_) {
831                 MathCursorPos i1;
832                 MathCursorPos i2;
833                 getSelection(i1, i2); 
834                 if (i1.idx_ == i2.idx_) {
835                         MathArray & ar = i1.cell();
836                         for (MathInset::pos_type pos = i1.pos_; pos != i2.pos_; ++pos)
837                                 ar.at(pos)->handleFont(t);
838                 }
839         } else 
840                 lastcode_ = (lastcode_ == t) ? LM_TC_VAR : t;
841 }
842
843
844 void MathCursor::handleDelim(string const & l, string const & r)
845 {
846         handleNest(new MathDelimInset(l, r));
847 }
848
849
850 void MathCursor::handleNest(MathInset * p)
851 {
852         if (selection_) {
853                 selCut();
854                 p->cell(0) = theSelection.glue();
855         }
856         insert(MathAtom(p)); // this invalidates p!
857         pushRight(prevAtom());
858 }
859
860
861 void MathCursor::getPos(int & x, int & y)
862 {
863 #ifdef WITH_WARNINGS
864 #warning This should probably take cellXOffset and cellYOffset into account
865 #endif
866         x = xarray().xo() + xarray().pos2x(pos());
867         y = xarray().yo();
868 }
869
870
871 MathAtom & MathCursor::par() const
872 {
873         return *cursor().par_;
874 }
875
876
877 InsetFormulaBase const * MathCursor::formula()
878 {
879         return formula_;
880 }
881
882
883 MathCursor::idx_type MathCursor::idx() const
884 {
885         return cursor().idx_;
886 }
887
888
889 MathCursor::idx_type & MathCursor::idx()
890 {
891         return cursor().idx_;
892 }
893
894
895 MathCursor::pos_type MathCursor::pos() const
896 {
897         return cursor().pos_;
898 }
899
900
901 MathCursor::pos_type & MathCursor::pos()
902 {
903         return cursor().pos_;
904 }
905
906
907 bool MathCursor::inMacroMode() const
908 {
909         return macroNamePos() != -1;
910 }
911
912
913 bool MathCursor::selection() const
914 {
915         return selection_;
916 }
917
918
919 MathArrayInset * MathCursor::enclosingArray(MathCursor::idx_type & idx) const
920 {
921         for (int i = Cursor_.size() - 1; i >= 0; --i) {
922                 MathArrayInset * p = (*Cursor_[i].par_)->asArrayInset();
923                 if (p) {
924                         idx = Cursor_[i].idx_;
925                         return p;
926                 }
927         }
928         return 0;
929 }
930
931
932 void MathCursor::pullArg(bool goright)
933 {
934         dump("pullarg");
935         MathArray a = array();
936
937         MathScriptInset const * p = par()->asScriptInset();
938         if (p) {
939                 // special handling for scripts
940                 const bool up = p->hasUp();
941                 popLeft();
942                 MathScriptInset * q = nextAtom()->asScriptInset();
943                 if (q)
944                         q->removeScript(up);
945                 ++pos();
946                 array().insert(pos(), a);
947                 return;
948         }
949
950         if (popLeft()) {
951                 plainErase();
952                 array().insert(pos(), a);
953                 if (goright) 
954                         pos() += a.size();
955         }
956 }
957
958
959 void MathCursor::normalize() const
960 {
961 #ifdef WITH_WARNINGS
962 #warning This is evil!
963 #endif
964         MathCursor * it = const_cast<MathCursor *>(this);
965
966         if (idx() >= par()->nargs()) {
967                 lyxerr << "this should not really happen - 1: "
968                        << idx() << " " << par()->nargs() << "\n";
969                 dump("error 2");
970         }
971         it->idx()    = min(idx(), par()->nargs() - 1);
972
973         if (pos() > size()) {
974                 lyxerr << "this should not really happen - 2: "
975                         << pos() << " " << size() <<  " in idx: " << it->idx()
976                         << " in atom: '";
977                 WriteStream wi(0, lyxerr, false);
978                 it->par()->write(wi);
979                 lyxerr << "\n";
980                 dump("error 4");
981         }
982         it->pos() = min(pos(), size());
983 }
984
985
986 MathCursor::size_type MathCursor::size() const
987 {
988         return array().size();
989 }
990
991
992 MathCursor::col_type MathCursor::col() const
993 {
994         return par()->col(idx());
995 }
996
997
998 MathCursor::row_type MathCursor::row() const
999 {
1000         return par()->row(idx());
1001 }
1002
1003
1004 bool MathCursor::hasPrevAtom() const
1005 {
1006         return pos() > 0;
1007 }
1008
1009
1010 bool MathCursor::hasNextAtom() const
1011 {
1012         return pos() < size();
1013 }
1014
1015
1016 MathAtom const & MathCursor::prevAtom() const
1017 {
1018         lyx::Assert(pos() > 0);
1019         return array().at(pos() - 1);
1020 }
1021
1022
1023 MathAtom & MathCursor::prevAtom()
1024 {
1025         lyx::Assert(pos() > 0);
1026         return array().at(pos() - 1);
1027 }
1028
1029
1030 MathAtom const & MathCursor::nextAtom() const
1031 {
1032         lyx::Assert(pos() < size());
1033         return array().at(pos());
1034 }
1035
1036
1037 MathAtom & MathCursor::nextAtom()
1038 {
1039         lyx::Assert(pos() < size());
1040         return array().at(pos());
1041 }
1042
1043
1044 MathArray & MathCursor::array() const
1045 {
1046         static MathArray dummy;
1047
1048         if (idx() >= par()->nargs()) {
1049                 lyxerr << "############  idx_ " << idx() << " not valid\n";
1050                 return dummy;
1051         }
1052
1053         return cursor().cell();
1054 }
1055
1056
1057 MathXArray & MathCursor::xarray() const
1058 {
1059         return cursor().xcell();
1060 }
1061
1062
1063 void MathCursor::idxNext()
1064 {
1065         par()->idxNext(idx(), pos());
1066 }
1067
1068
1069 void MathCursor::idxPrev()
1070 {
1071         par()->idxPrev(idx(), pos());
1072 }
1073
1074
1075 void MathCursor::splitCell()
1076 {
1077         if (idx() == par()->nargs() - 1) 
1078                 return;
1079         MathArray ar = array();
1080         ar.erase(0, pos());
1081         array().erase(pos(), size());
1082         ++idx();
1083         pos() = 0;
1084         array().insert(0, ar);
1085 }
1086
1087
1088 void MathCursor::breakLine()
1089 {
1090         // leave inner cells
1091         while (popRight())
1092                 ;
1093
1094         MathHullInset * p = formula()->par()->asHullInset();
1095         if (!p)
1096                 return;
1097
1098         if (p->getType() == LM_OT_SIMPLE || p->getType() == LM_OT_EQUATION) {
1099                 p->mutate(LM_OT_EQNARRAY);
1100                 idx() = 0;
1101                 pos() = size();
1102         } else {
1103                 p->addRow(row());
1104
1105                 // split line
1106                 const row_type r = row();
1107                 for (col_type c = col() + 1; c < p->ncols(); ++c)
1108                         p->cell(p->index(r, c)).swap(p->cell(p->index(r + 1, c)));
1109
1110                 // split cell
1111                 splitCell();
1112                 p->cell(idx()).swap(p->cell(idx() + p->ncols() - 1));
1113         }
1114 }
1115
1116
1117 char MathCursor::valign() const
1118 {
1119         idx_type idx;
1120         MathArrayInset * p = enclosingArray(idx);
1121         return p ? p->valign() : '\0';
1122 }
1123
1124
1125 char MathCursor::halign() const
1126 {
1127         idx_type idx;
1128         MathArrayInset * p = enclosingArray(idx);
1129         return p ? p->halign(idx % p->ncols()) : '\0';
1130 }
1131
1132
1133 void MathCursor::getSelection(MathCursorPos & i1, MathCursorPos & i2) const
1134 {
1135         MathCursorPos anc = normalAnchor();
1136         if (anc < cursor()) {
1137                 i1 = anc;
1138                 i2 = cursor();
1139         } else {
1140                 i1 = cursor();
1141                 i2 = anc;
1142         }
1143 }
1144
1145
1146 MathCursorPos & MathCursor::cursor()
1147 {
1148         return Cursor_.back();
1149 }
1150
1151
1152 MathCursorPos const & MathCursor::cursor() const
1153 {
1154         return Cursor_.back();
1155 }
1156
1157
1158 int MathCursor::cellXOffset() const
1159 {
1160         return par()->cellXOffset(idx());
1161 }
1162
1163
1164 int MathCursor::cellYOffset() const
1165 {
1166         return par()->cellYOffset(idx());
1167 }
1168
1169
1170 int MathCursor::xpos() const
1171 {
1172         return cellXOffset() + xarray().pos2x(pos());
1173 }
1174
1175
1176 int MathCursor::ypos() const
1177 {
1178         return cellYOffset();
1179 }
1180
1181
1182
1183 void MathCursor::gotoX(int x) 
1184 {
1185         pos() = xarray().x2pos(x - cellXOffset());
1186 }
1187
1188
1189 bool MathCursor::goUp()
1190 {
1191         // first ask the inset if it knows better then we
1192         if (par()->idxUp(idx(), pos()))
1193                 return true;
1194
1195         // leave subscript to the nearest side  
1196         MathScriptInset * p = par()->asScriptInset();
1197         if (p && idx() == 0) {
1198                 if (pos() <= size() / 2)
1199                         popLeft();
1200                 else
1201                         popRight();             
1202                 return true;
1203         }
1204
1205         // if not, apply brute force.
1206         int x0;
1207         int y0;
1208         getPos(x0, y0);
1209         std::vector<MathCursorPos> save = Cursor_;
1210         y0 -= xarray().ascent();
1211         for (int y = y0 - 4; y > formula()->upperY(); y -= 4) {
1212                 setPos(x0, y);
1213                 if (save != Cursor_ && xarray().yo() < y0)
1214                         return true;    
1215         }
1216         Cursor_ = save;
1217         return false;
1218 }
1219
1220
1221 bool MathCursor::goDown()
1222 {
1223         // first ask the inset if it knows better then we
1224         if (par()->idxDown(idx(), pos()))
1225                 return true;
1226
1227         // leave superscript to the nearest side        
1228         MathScriptInset * p = par()->asScriptInset();
1229         if (p && idx() == 1) {
1230                 if (pos() <= size() / 2)
1231                         popLeft();
1232                 else
1233                         popRight();             
1234                 return true;
1235         }
1236
1237         // if not, apply brute force.
1238         int x0;
1239         int y0;
1240         getPos(x0, y0);
1241         std::vector<MathCursorPos> save = Cursor_;
1242         y0 += xarray().descent();
1243         for (int y = y0 + 4; y < formula()->lowerY(); y += 4) {
1244                 setPos(x0, y);
1245                 if (save != Cursor_ && xarray().yo() > y0)
1246                         return true;    
1247         }
1248         Cursor_ = save;
1249         return false;
1250 }
1251
1252
1253 bool MathCursor::idxLeft()
1254 {
1255         return par()->idxLeft(idx(), pos());
1256 }
1257
1258
1259 bool MathCursor::idxRight()
1260 {
1261         return par()->idxRight(idx(), pos());
1262 }
1263
1264
1265 void MathCursor::interpret(string const & s)
1266 {
1267         //lyxerr << "interpret 1: '" << s << "'\n";
1268         if (s.empty())
1269                 return;
1270
1271         if (s.size() == 1) {
1272                 interpret(s[0]);
1273                 return;
1274         }
1275
1276         //lyxerr << "char: '" << s[0] << "'  int: " << int(s[0]) << endl;
1277         //owner_->getIntl()->getTrans().TranslateAndInsert(s[0], lt);   
1278         //lyxerr << "trans: '" << s[0] << "'  int: " << int(s[0]) << endl;
1279
1280         if (s.size() > 7 && s.substr(0, 7) == "matrix ") {
1281                 unsigned int m = 1;
1282                 unsigned int n = 1;
1283                 string v_align;
1284                 string h_align;
1285                 istringstream is(s.substr(7).c_str());
1286                 is >> m >> n >> v_align >> h_align;
1287                 m = std::max(1u, m);
1288                 n = std::max(1u, n);
1289                 v_align += 'c';
1290                 niceInsert(MathAtom(new MathArrayInset(m, n, v_align[0], h_align)));
1291                 return;
1292         }
1293
1294         if (s == "\\over" || s == "\\choose" || s == "\\atop") {
1295                 MathArray ar = array();
1296                 MathAtom t = createMathInset(s.substr(1));
1297                 t->asNestInset()->cell(0).swap(array());
1298                 pos() = 0;
1299                 niceInsert(t);
1300                 popRight();
1301                 left();
1302                 return;
1303         }
1304
1305         niceInsert(createMathInset(s.substr(1)));
1306 }
1307
1308
1309 void MathCursor::interpret(char c)
1310 {
1311         //lyxerr << "interpret 2: '" << c << "'\n";
1312
1313         if (inMacroMode()) {
1314                 string name = macroName();
1315
1316                 if (name == "\\" && c == '#') {
1317                         insert(c, LM_TC_TEX);
1318                         return;
1319                 }
1320
1321                 if (name == "\\" && c == '\\') {
1322                         backspace();
1323                         interpret("\\backslash");
1324                         return;
1325                 }
1326
1327                 if (name == "\\#" && '1' <= c && c <= '9') {
1328                         insert(c, LM_TC_TEX);
1329                         macroModeClose();
1330                         return;
1331                 }
1332
1333                 if (isalpha(c)) {
1334                         insert(c, LM_TC_TEX);
1335                         return;
1336                 }
1337
1338                 if (name == "\\") {
1339                         insert(c, LM_TC_TEX);
1340                         macroModeClose();
1341                         return;
1342                 }
1343
1344                 macroModeClose();
1345                 return;
1346         }
1347
1348         // no macro mode
1349         if (c == '^' || c == '_') {
1350                 const bool up = (c == '^');
1351                 selCut();
1352                 if (hasPrevAtom() && prevAtom()->asScriptInset()) {
1353                         prevAtom()->asScriptInset()->ensure(up);
1354                         pushRight(prevAtom());
1355                         idx() = up;
1356                         pos() = size();
1357                 } else if (hasNextAtom() && nextAtom()->asScriptInset()) {
1358                         nextAtom()->asScriptInset()->ensure(up);
1359                         pushLeft(nextAtom());
1360                         idx() = up;
1361                         pos() = 0;
1362                 } else {
1363                         plainInsert(MathAtom(new MathScriptInset(up)));
1364                         prevAtom()->asScriptInset()->ensure(up);
1365                         pushRight(prevAtom());
1366                         idx() = up;
1367                         pos() = 0;
1368                 }
1369                 selPaste();
1370                 dump("1");
1371                 return;
1372         }
1373
1374         if (selection_)
1375                 selDel();
1376
1377         if (lastcode_ == LM_TC_TEXTRM) {
1378                 // suppress direct insertion of to spaces in a row
1379                 // the still allows typing  '<space>a<space>' and deleting the 'a', but
1380                 // it is better than nothing
1381                 if (hasPrevAtom() && prevAtom()->getChar() == ' ')
1382                         return;
1383                 insert(c, LM_TC_TEXTRM);
1384                 return;
1385         }
1386
1387         if (c == ' ') {
1388                 if (hasPrevAtom() && prevAtom()->asSpaceInset()) {
1389                         prevAtom()->asSpaceInset()->incSpace();
1390                         return;
1391                 }
1392
1393                 mathcursor->popRight();
1394                 return;
1395         }
1396
1397 /*
1398         if (strchr("{}", c)) {
1399                 insert(c, LM_TC_TEX);
1400                 return;
1401         }
1402 */
1403
1404         if (c == '{') {
1405                 niceInsert(MathAtom(new MathBraceInset));
1406                 return;
1407         }
1408
1409         if (c == '}') {
1410                 return;
1411         }
1412
1413         if (strchr("#$%", c)) {
1414                 insert(MathAtom(new MathSpecialCharInset(c)));  
1415                 lastcode_ = LM_TC_VAR;
1416                 return;
1417         }
1418
1419         if (isalpha(c) && lastcode_ == LM_TC_GREEK) {
1420                 insert(c, LM_TC_VAR);
1421                 return; 
1422         }
1423
1424         if (isalpha(c) && lastcode_ == LM_TC_GREEK1) {
1425                 insert(c, LM_TC_VAR);
1426                 lastcode_ = LM_TC_VAR;
1427                 return; 
1428         }
1429
1430         if (c == '\\') {
1431                 insert(c, LM_TC_TEX);
1432                 //bv->owner()->message(_("TeX mode"));
1433                 return; 
1434         }
1435
1436         // no special circumstances, so insert the character without any fuss
1437         insert(c, LM_TC_MIN);
1438 }
1439
1440
1441
1442 ////////////////////////////////////////////////////////////////////////
1443
1444
1445 bool operator==(MathCursorPos const & ti, MathCursorPos const & it)
1446 {
1447         return ti.par_ == it.par_ && ti.idx_ == it.idx_ && ti.pos_ == it.pos_;
1448 }
1449
1450
1451 bool operator<(MathCursorPos const & ti, MathCursorPos const & it)
1452 {
1453         if (ti.par_ != it.par_) {
1454                 lyxerr << "can't compare cursor and anchor in different insets\n";
1455                 return true;
1456         }
1457         if (ti.idx_ != it.idx_)
1458                 return ti.idx_ < it.idx_;
1459         return ti.pos_ < it.pos_;
1460 }
1461
1462
1463 MathArray & MathCursorPos::cell(MathCursor::idx_type idx) const
1464 {
1465         return (*par_)->cell(idx);
1466 }
1467
1468
1469 MathArray & MathCursorPos::cell() const
1470 {
1471         return (*par_)->cell(idx_);
1472 }
1473
1474
1475 MathXArray & MathCursorPos::xcell(MathCursor::idx_type idx) const
1476 {
1477         return (*par_)->xcell(idx);
1478 }
1479
1480
1481 MathXArray & MathCursorPos::xcell() const
1482 {
1483         return (*par_)->xcell(idx_);
1484 }
1485
1486
1487 MathCursorPos MathCursor::normalAnchor() const
1488 {
1489         // use Anchor on the same level as Cursor
1490         MathCursorPos normal = Anchor_[Cursor_.size() - 1];
1491         if (Cursor_.size() < Anchor_.size() && !(normal < cursor())) {
1492                 // anchor is behind cursor -> move anchor behind the inset
1493                 ++normal.pos_;
1494         }
1495         return normal;
1496 }
1497
1498
1499 void MathCursor::stripFromLastEqualSign()
1500 {
1501         // find position of last '=' in the array
1502         MathArray & ar = cursor().cell();
1503         MathArray::const_iterator et = ar.end();
1504         for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
1505                 if ((*it)->getChar() == '=')
1506                         et = it;
1507
1508         // delete everything behind this position
1509         ar.erase(et - ar.begin(), ar.size());
1510         pos() = ar.size(); 
1511 }
1512
1513