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