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