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