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