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