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