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