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