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