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