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