]> git.lyx.org Git - lyx.git/blob - src/mathed/math_cursor.C
db86c379c19a4a5a58c88cf287c8561183ffa896
[lyx.git] / src / mathed / math_cursor.C
1 /*
2  *  File:        math_cursor.C
3  *  Purpose:     Interaction for mathed
4  *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5  *  Created:     January 1996
6  *  Description: Math interaction for a WYSIWYG math editor.
7  *
8  *  Dependencies: Xlib, XForms
9  *
10  *  Copyright: 1996, Alejandro Aguilar Sierra
11  *
12  *   Version: 0.8beta, Math & Lyx project.
13  *
14  *   You are free to use and modify this code under the terms of
15  *   the GNU General Public Licence version 2 or later.
16  */
17
18 #ifdef __GNUG__
19 #pragma implementation
20 #endif
21
22 #include <config.h>
23 #include <algorithm>
24 #include <cctype>
25
26 #include "debug.h"
27 #include "LColor.h"
28 #include "Painter.h"
29 #include "mathed/support.h"
30 #include "formulabase.h"
31 #include "math_cursor.h"
32 #include "math_arrayinset.h"
33 #include "math_bigopinset.h"
34 #include "math_symbolinset.h"
35 #include "math_decorationinset.h"
36 #include "math_deliminset.h"
37 #include "math_dotsinset.h"
38 #include "math_fracinset.h"
39 #include "math_funcinset.h"
40 #include "math_funcliminset.h"
41 #include "math_gridinset.h"
42 #include "math_macro.h"
43 #include "math_macroarg.h"
44 #include "math_macrotable.h"
45 #include "math_macrotemplate.h"
46 #include "math_matrixinset.h"
47 #include "math_rootinset.h"
48 #include "math_spaceinset.h"
49 #include "math_sqrtinset.h"
50 #include "support/lstrings.h"
51 #include "math_scriptinset.h"
52 #include "math_parser.h"
53
54 using std::endl;
55 using std::min;
56 using std::max;
57 using std::isalnum;
58
59
60 namespace {
61
62 struct Selection
63 {
64         void grab(MathCursor const & cursor)
65         {
66                 data_.clear();
67                 MathCursorPos i1;
68                 MathCursorPos i2;
69                 cursor.getSelection(i1, i2); 
70                 if (i1.idx_ == i2.idx_)
71                         data_.push_back(MathArray(i1.cell(), i1.pos_, i2.pos_));
72                 else {
73                         std::vector<int> indices = i1.par_->idxBetween(i1.idx_, i2.idx_);
74                         for (unsigned i = 0; i < indices.size(); ++i)
75                                 data_.push_back(i1.cell(indices[i]));
76                 }
77         }
78
79         void erase(MathCursor & cursor)
80         {
81                 MathCursorPos i1;
82                 MathCursorPos i2;
83                 cursor.getSelection(i1, i2); 
84                 if (i1.idx_ == i2.idx_) {
85                         i1.cell().erase(i1.pos_, i2.pos_);
86                 } else {
87                         std::vector<int> indices = i1.par_->idxBetween(i1.idx_, i2.idx_);
88                         for (unsigned i = 0; i < indices.size(); ++i)
89                                 i1.cell(indices[i]).erase();
90                 }
91                 cursor.cursor() = i1;
92         }
93
94         void paste(MathCursor & cursor) const
95         {
96                 cursor.insert(glue());
97         }
98
99         // glues selection to one cell
100         MathArray glue() const
101         {
102                 MathArray ar;
103                 for (unsigned i = 0; i < data_.size(); ++i)
104                         ar.push_back(data_[i]);
105                 return ar;
106         }
107
108         void clear()
109         {
110                 data_.clear();
111         }
112
113         std::vector<MathArray> data_;
114 };
115
116
117 Selection theSelection;
118
119
120 std::ostream & operator<<(std::ostream & os, MathCursorPos const & p)
121 {
122         os << "(par: " << p.par_ << " idx: " << p.idx_
123            << " pos: " << p.pos_ << ")";
124         return os;
125 }
126
127 }
128
129
130 MathCursor::MathCursor(InsetFormulaBase * formula)
131         : formula_(formula), lastcode_(LM_TC_MIN), imacro_(0), selection_(false)
132 {
133         first();
134 }
135
136
137 MathCursor::~MathCursor()
138 {
139         delete imacro_;
140 }
141
142 void MathCursor::push(MathInset * par, bool first)
143 {
144         MathCursorPos p;
145         p.par_ = par;
146         if (first)
147                 par->idxFirst(p.idx_, p.pos_);
148         else
149                 par->idxLast(p.idx_, p.pos_);
150         Cursor_.push_back(p);
151 }
152
153
154 bool MathCursor::pop()
155 {
156         if (Cursor_.size() <= 1)
157                 return false;
158         Cursor_.pop_back();
159         return true;
160 }
161
162
163 MathInset * MathCursor::parInset(int i) const
164 {
165         return Cursor_[i].par_;
166 }
167
168
169 void MathCursor::dump(char const * what) const
170 {
171         return;
172
173         lyxerr << "MC: " << what << "\n";
174         for (unsigned i = 0; i < Cursor_.size(); ++i)
175                 lyxerr << "  i: " << i 
176                         << " pos: " << Cursor_[i].pos_
177                         << " idx: " << Cursor_[i].idx_
178                         << " par: " << Cursor_[i].par_ << "\n";
179
180         //lyxerr        << " sel: " << selection_ << " data: " << array() << "\n";
181 }
182
183
184 void MathCursor::seldump(char const * str) const
185 {
186         //lyxerr << "SEL: " << str << ": '" << theSelection << "'\n";
187         //dump("   Pos");
188         return;
189
190         lyxerr << "\n\n\n=================vvvvvvvvvvvvv=======================   "
191                 <<  str << "\ntheSelection: " << selection_
192                 << " '" << theSelection.glue() << "'\n";
193         for (unsigned int i = 0; i < Cursor_.size(); ++i) 
194                 lyxerr << Cursor_[i].par_ << "\n'" << Cursor_[i].cell() << "'\n";
195         lyxerr << "\n";
196         for (unsigned int i = 0; i < Anchor_.size(); ++i) 
197                 lyxerr << Anchor_[i].par_ << "\n'" << Anchor_[i].cell() << "'\n";
198         //lyxerr << "\ncursor.pos_: " << pos();
199         //lyxerr << "\nanchor.pos_: " << anchor().pos_;
200         lyxerr << "\n===================^^^^^^^^^^^^=====================\n\n\n";
201 }
202
203
204 bool MathCursor::isInside(MathInset const * p) const
205 {
206         for (unsigned i = 0; i < Cursor_.size(); ++i) 
207                 if (parInset(i) == p) 
208                         return true;
209         return false;
210 }
211
212
213 bool MathCursor::openable(MathInset * p, bool sel, bool useupdown) const
214 {
215         if (!p)
216                 return false;
217
218         if (!(p->isActive() || (useupdown && p->isScriptInset())))
219                 return false;
220
221         if (sel) {
222                 // we can't move into anything new during selection
223                 if (Cursor_.size() == Anchor_.size())
224                         return false;
225                 if (p != Anchor_[Cursor_.size()].par_)
226                         return false;
227         }
228         return true;
229 }
230
231
232 void MathCursor::plainLeft()
233 {
234         --cursor().pos_;
235 }
236
237
238 void MathCursor::plainRight()
239 {
240         ++cursor().pos_;
241 }
242
243
244 bool MathCursor::left(bool sel)
245 {
246         dump("Left 1");
247         if (imacro_) {
248                 // was MacroModeBack()
249                 if (!imacro_->name().empty()) {
250                         imacro_->setName(imacro_->name().substr(0, imacro_->name().length()-1));
251                         imacro_->metrics(imacro_->size());
252                 } else
253                         macroModeClose();
254                 return true;
255         }
256         selHandle(sel);
257         clearLastCode();
258
259         MathInset * p = prevInset();
260         if (openable(p, sel, false)) {
261                 plainLeft();
262                 push(p, false);
263                 return true;
264         } 
265         if (pos()) {
266                 plainLeft();
267                 return true;
268         }
269         if (par()->idxLeft(idx(), pos()))
270                 return true;
271         if (pop())
272                 return true;
273         return false;
274 }
275
276
277 bool MathCursor::right(bool sel)
278 {
279         dump("Right 1");
280         if (imacro_) {
281                 macroModeClose();
282                 return true;
283         }
284         selHandle(sel);
285         clearLastCode();
286
287         MathInset * p = nextInset();
288         if (openable(p, sel, false)) {
289                 push(p, true);
290                 return true;
291         }
292         if (pos() != array().size()) {
293                 plainRight();
294                 return true;
295         }
296         if (par()->idxRight(idx(), pos())) {
297                 return true;
298         }
299         if (pop()) {
300                 plainRight();
301                 return true;
302         }
303         return false;
304 }
305
306
307 void MathCursor::first()
308 {
309         Cursor_.clear();
310         push(outerPar(), true);
311 }
312
313
314 void MathCursor::last()
315 {
316         Cursor_.clear();
317         push(outerPar(), false);
318 }
319
320
321 void MathCursor::setPos(int x, int y)
322 {
323         dump("setPos 1");
324         //lyxerr << "MathCursor::setPos x: " << x << " y: " << y << "\n";
325
326         macroModeClose();
327         lastcode_ = LM_TC_MIN;
328         first();
329
330         cursor().par_  = outerPar();
331
332         while (1) {
333                 idx() = -1;
334                 cursor().pos_ = -1;
335                 //lyxerr << "found idx: " << idx_ << " cursor: " << pos()  << "\n";
336                 int distmin = 1 << 30; // large enough
337                 for (int i = 0; i < par()->nargs(); ++i) {
338                         MathXArray const & ar = par()->xcell(i);
339                         int x1 = x - ar.xo();
340                         int y1 = y - ar.yo();
341                         int c  = ar.x2pos(x1);
342                         int xx = abs(x1 - ar.pos2x(c));
343                         int yy = abs(y1);
344                         //lyxerr << "idx: " << i << " xx: " << xx << " yy: " << yy
345                         //      << " c: " << c  << " xo: " << ar.xo() << "\n";
346                         if (yy + xx <= distmin) {
347                                 distmin        = yy + xx;
348                                 idx()  = i;
349                                 pos()  = c;
350                         }
351                 }
352                 //lyxerr << "found idx: " << idx() << " cursor: "
353                 //      << pos()  << "\n";
354                 MathInset * n = nextInset();
355                 MathInset * p = prevInset();
356                 if (openable(n, selection_, true) && n->covers(x, y))
357                         push(n, true);
358                 else if (openable(p, selection_, true) && p->covers(x, y)) {
359                         plainLeft();
360                         push(p, false);
361                 } else 
362                         break;
363         }
364         dump("setPos 2");
365 }
366
367
368 void MathCursor::home()
369 {
370         dump("home 1");
371         macroModeClose();
372         clearLastCode();
373         if (!par()->idxHome(idx(), pos())) 
374                 pop();
375         dump("home 2");
376 }
377
378
379 void MathCursor::end()
380 {
381         dump("end 1");
382         macroModeClose();
383         clearLastCode();
384         if (!par()->idxEnd(idx(), pos())) {
385                 pop();
386                 ++pos();
387         }
388         dump("end 2");
389 }
390
391
392 void MathCursor::plainErase()
393 {
394         array().erase(pos());
395 }
396
397
398 void MathCursor::insert(char c, MathTextCodes t)
399 {
400         //lyxerr << "inserting '" << c << "'\n";
401         if (selection_)
402                 selDel();
403
404         if (t != LM_TC_MIN)
405                 lastcode_ = t;
406
407         if (imacro_ && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
408                 macroModeClose();
409
410         if (imacro_) {
411                 if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
412                         // was MacroModeinsert(c);
413                         imacro_->setName(imacro_->name() + c);
414                         return;
415                 }
416         }
417
418         array().insert(pos(), c, t);
419         plainRight();
420 }
421
422
423 void MathCursor::insert(MathInset * p)
424 {
425         macroModeClose();
426
427         if (selection_) {
428                 if (p->nargs())
429                         selCut();
430                 else
431                         selDel();
432         }
433
434         array().insert(pos(), p);
435         plainRight();
436 }
437
438
439 void MathCursor::insert(MathArray const & ar)
440 {
441         macroModeClose();
442         if (selection_)
443                 selCut();
444
445         array().insert(pos(), ar);
446         pos() += ar.size();
447 }
448
449
450 void MathCursor::backspace()
451 {
452         if (inMacroMode()) {
453                 left();
454                 return;
455         }
456
457         if (pos()) {
458                 plainLeft();
459                 plainErase();
460                 return;
461         }
462
463         if (array().size()) {
464                 pullArg(false);
465                 return;
466         }
467
468         erase();
469 }
470
471
472 void MathCursor::erase()
473 {
474         dump("erase 1");
475         if (imacro_)
476                 return;
477
478         if (selection_) {
479                 selDel();
480                 return;
481         }
482
483         // delete empty cells if necessary
484         if (pos() == 0 && array().empty()) {
485                 bool popit;
486                 bool removeit;
487                 par()->idxDelete(idx(), popit, removeit);
488                 if (popit && pop() && removeit)
489                         plainErase();
490                 return;
491         }
492
493         if (pos() < array().size())
494                 plainErase();
495
496         dump("erase 2");
497 }
498
499
500 void MathCursor::delLine()
501 {
502         macroModeClose();
503
504         if (selection_) {
505                 selDel();
506                 return;
507         }
508
509         if (par()->nrows() > 1)
510                 par()->delRow(row());
511 }
512
513
514 bool MathCursor::up(bool sel)
515 {
516         dump("up 1");
517         macroModeClose();
518         selHandle(sel);
519
520         if (selection_) {
521                 int x = xarray().pos2x(pos());
522                 if (cursor().idxDown()) {
523                         pos() = xarray().x2pos(x);
524                         return true;
525                 }
526                 if (pop()) 
527                         return true;
528                 return false;
529         }
530
531         // check whether we could move into an inset on the right or on the left
532         MathInset * p = nextInset();
533         if (p) {
534                 int idxx, poss;
535                 if (p->idxFirstUp(idxx, poss)) {
536                         push(p, true);
537                         idx() = idxx;
538                         pos() = poss;
539                         dump("up 3");
540                         return true;
541                 }
542         }
543
544         p = prevInset();
545         if (p) {
546                 int idxx, poss;
547                 if (p->idxLastUp(idxx, poss)) {
548                         plainLeft();
549                         push(p, false);
550                         idx() = idxx;
551                         pos() = poss;
552                         dump("up 4");
553                         return true;
554                 }
555         }
556
557         int x = xarray().pos2x(pos());
558         if (cursor().idxUp()) {
559                 pos() = xarray().x2pos(x);
560                 return true;
561         }
562         if (pop())
563                 return true;
564         return false;
565 }
566
567
568 bool MathCursor::down(bool sel)
569 {
570         dump("down 1");
571         macroModeClose();
572         selHandle(sel);
573
574         if (selection_) {
575                 int x = xarray().pos2x(pos());
576                 if (cursor().idxDown()) {
577                         pos() = xarray().x2pos(x);
578                         return true;
579                 }
580                 if (pop()) 
581                         return true;
582                 return false;
583         }
584
585         // check whether we could move into an inset on the right or on the left
586         MathInset * p = nextInset();
587         if (p) {
588                 int idxx = 0;
589                 int poss = 0;
590                 if (p->idxFirstDown(idxx, poss)) {
591                         push(p, true);
592                         idx() = idxx;
593                         pos() = poss;
594                         dump("Down 3");
595                         return true;
596                 }
597         }
598
599         p = prevInset();
600         if (p) {
601                 int idxx = 0;
602                 int poss = 0;
603                 if (p->idxLastDown(idxx, poss)) {
604                         plainLeft();
605                         push(p, false);
606                         idx() = idxx;
607                         pos() = poss;
608                         dump("Down 4");
609                         return true;
610                 }
611         }
612
613         int x = xarray().pos2x(pos());
614         if (cursor().idxDown()) {
615                 pos() = xarray().x2pos(x);
616                 return true;
617         }
618         if (pop())
619                 return true;
620         return false;
621 }
622
623
624 bool MathCursor::toggleLimits()
625 {
626         MathScriptInset * p = prevScriptInset();
627         if (!p)
628                 return false;
629         int old = p->limits();
630         p->limits(old < 0 ? 1 : -1);
631         return old != p->limits();
632 }
633
634
635 void MathCursor::setSize(MathStyles size)
636 {
637         par()->userSetSize(size);
638 }
639
640
641
642 void MathCursor::interpret(string const & s)
643 {
644         //lyxerr << "interpret: '" << s << "'\n";
645         //lyxerr << "in: " << in_word_set(s) << " \n";
646
647         if (s.empty())
648                 return;
649
650         if (s[0] == '^' || s[0] == '_') {
651                 bool const up = (s[0] == '^');
652                 selCut();       
653                 MathScriptInset * p = prevScriptInset();
654                 if (!p) {
655                         MathInset * b = prevInset();
656                         if (b && b->isScriptable()) {
657                                 p = new MathScriptInset(up, !up, b->clone());
658                                 plainLeft();
659                                 plainErase();
660                         } else {
661                                 p = new MathScriptInset(up, !up);
662                         }
663                         insert(p);
664                 }
665                 plainLeft();
666                 push(p, true);
667                 if (up)
668                         p->up(true);
669                 else
670                         p->down(true);
671                 idx() = up ? 0 : 1;
672                 pos() = 0;
673                 selPaste();
674                 return;
675         }
676
677         if (s[0] == '!' || s[0] == ','  || s[0] == ':' || s[0] == ';') {
678                 int sp = (s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0));
679                 insert(new MathSpaceInset(sp));
680                 return;
681         }
682
683         MathInset * p = 0;
684         latexkeys const * l = in_word_set(s);
685
686         if (l == 0) {
687                 if (s == "root") 
688                         p = new MathRootInset;
689                 else if (MathMacroTable::hasTemplate(s))
690                         p = new MathMacro(MathMacroTable::provideTemplate(s));
691                 else if (s.size() > 7 && s.substr(0, 7) == "matrix ") {
692                         int m = 1;
693                         int n = 1;
694                         string v_align;
695                         string h_align;
696                         istringstream is(s.substr(7).c_str());
697                         is >> m >> n >> v_align >> h_align;
698                         m = std::max(1, m);
699                         n = std::max(1, n);
700                         v_align += 'c';
701                         MathArrayInset * pp = new MathArrayInset(m, n);
702                         pp->valign(v_align[0]);
703                         pp->halign(h_align);
704                         p = pp;
705                 }
706                 else
707                         p = new MathFuncInset(s);
708         } else {
709                 switch (l->token) {
710                         case LM_TK_BIGSYM:
711                                 p = new MathBigopInset(l);
712                                 break;
713
714                         case LM_TK_FUNCLIM:
715                                 p = new MathFuncLimInset(l);
716                                 break;
717
718                         case LM_TK_SYM: 
719                                 p = new MathSymbolInset(l);
720                                 break;
721
722                         case LM_TK_STACK:
723                                 p = new MathFracInset("stackrel");
724                                 break;
725
726                         case LM_TK_FRAC:
727                                 p = new MathFracInset("frac");
728                                 break;
729
730                         case LM_TK_SQRT:
731                                 p = new MathSqrtInset;
732                                 break;
733
734                         case LM_TK_DECORATION:
735                                 p = new MathDecorationInset(l);
736                                 break;
737
738                         case LM_TK_SPACE:
739                                 p = new MathSpaceInset(l->id);
740                                 break;
741
742                         case LM_TK_DOTS:
743                                 p = new MathDotsInset(l);
744                                 break;
745
746                         case LM_TK_MACRO:
747                                 p = new MathMacro(MathMacroTable::provideTemplate(s));
748                                 break;
749
750                         default:
751                                 p = new MathFuncInset(l->name);
752                                 break;
753                 }
754         }
755
756         if (p) {
757                 selCut();
758                 insert(p);
759                 if (p->nargs()) {
760                         plainLeft();
761                         right();  // do not push for e.g. MathSymbolInset
762                         selPaste();
763                 }
764                 p->metrics(p->size());
765         }
766 }
767
768
769 void MathCursor::macroModeOpen()
770 {
771         if (!imacro_) {
772                 imacro_ = new MathFuncInset("");
773                 array().insert(pos(), imacro_);
774                 ++pos();
775                 //insert(imacro_);
776         } else
777                 lyxerr << "Math Warning: Already in macro mode" << endl;
778 }
779
780
781 void MathCursor::macroModeClose()
782 {
783         if (imacro_) {
784                 string name = imacro_->name();
785                 plainLeft();
786                 plainErase();
787                 imacro_ = 0;
788                 interpret(name);
789         }
790 }
791
792
793 void MathCursor::selCopy()
794 {
795         seldump("selCopy");
796         if (selection_) {
797                 theSelection.grab(*this);
798                 selClear();
799         }
800 }
801
802
803 void MathCursor::selCut()
804 {
805         seldump("selCut");
806         if (selection_) {
807                 theSelection.grab(*this);
808                 theSelection.erase(*this);
809                 selClear();
810         } else {
811                 theSelection.clear();
812         }
813 }
814
815
816 void MathCursor::selDel()
817 {
818         seldump("selDel");
819         if (selection_) {
820                 theSelection.erase(*this);
821                 selClear();
822         }
823 }
824
825
826 void MathCursor::selPaste()
827 {
828         seldump("selPaste");
829         theSelection.paste(*this);
830         selClear();
831 }
832
833
834 void MathCursor::selHandle(bool sel)
835 {
836         if (sel == selection_)
837                 return;
838
839         theSelection.clear();
840         Anchor_    = Cursor_;
841         selection_ = sel;
842 }
843
844
845 void MathCursor::selStart()
846 {
847         seldump("selStart");
848         if (selection_)
849                 return;
850
851         theSelection.clear();
852         Anchor_ = Cursor_;
853         selection_ = true;
854 }
855
856
857 void MathCursor::selClear()
858 {
859         seldump("selClear");
860         selection_ = false;
861 }
862
863
864 void MathCursor::drawSelection(Painter & pain) const
865 {
866         if (!selection_)
867                 return;
868
869         MathCursorPos i1;
870         MathCursorPos i2;
871         getSelection(i1, i2);
872
873         //lyxerr << "selection from: " << i1 << " to " << i2 << "\n";
874
875         if (i1.idx_ == i2.idx_) {
876                 MathXArray & c = i1.xcell();
877                 int x1 = c.xo() + c.pos2x(i1.pos_);
878                 int y1 = c.yo() - c.ascent();
879                 int x2 = c.xo() + c.pos2x(i2.pos_);
880                 int y2 = c.yo() + c.descent();
881                 pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
882         } else {
883
884                 std::vector<int> indices = i1.par_->idxBetween(i1.idx_, i2.idx_);
885                 for (unsigned i = 0; i < indices.size(); ++i) {
886                         MathXArray & c = i1.xcell(indices[i]);
887                         int x1 = c.xo();
888                         int y1 = c.yo() - c.ascent();
889                         int x2 = c.xo() + c.width();
890                         int y2 = c.yo() + c.descent();
891                         pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
892                 }
893         }
894 }
895
896
897 void MathCursor::handleFont(MathTextCodes t)
898 {
899         if (selection_) {
900                 MathCursorPos i1;
901                 MathCursorPos i2;
902                 getSelection(i1, i2); 
903                 if (i1.idx_ == i2.idx_) {
904                         MathArray & ar = i1.cell();
905                         for (int pos = i1.pos_; pos != i2.pos_; ++pos)
906                                 if (isalnum(ar.getChar(pos))) { 
907                                         MathTextCodes c = ar.getCode(pos) == t ? LM_TC_VAR : t;
908                                         ar.setCode(pos, c);
909                                 }
910                 }
911         } else 
912                 lastcode_ = (lastcode_ == t) ? LM_TC_VAR : t;
913 }
914
915
916 void MathCursor::handleAccent(string const & name)
917 {
918         latexkeys const * l = in_word_set(name);
919         if (!l)
920                 return;
921
922         MathDecorationInset * p = new MathDecorationInset(l);
923         if (selection_) {
924                 selCut();
925                 p->cell(0) = theSelection.glue();
926         }
927         insert(p);
928         push(p, true);
929 }
930
931
932 void MathCursor::handleDelim(int l, int r)
933 {
934         MathDelimInset * p = new MathDelimInset(l, r);
935         if (selection_) {
936                 selCut();
937                 p->cell(0) = theSelection.glue();
938         }
939         insert(p);
940         plainLeft();
941         push(p, true);
942 }
943
944
945 void MathCursor::getPos(int & x, int & y)
946 {
947         x = xarray().xo() + xarray().pos2x(pos());
948         y = xarray().yo();
949 }
950
951
952 MathTextCodes MathCursor::nextCode() const
953 {
954         return array().getCode(pos()); 
955 }
956
957
958 MathTextCodes MathCursor::prevCode() const
959 {
960         return array().getCode(pos() - 1); 
961 }
962
963
964 MathInset * MathCursor::par() const
965 {
966         return cursor().par_;
967 }
968
969
970 InsetFormulaBase const * MathCursor::formula()
971 {
972         return formula_;
973 }
974
975
976 int MathCursor::idx() const
977 {
978         return cursor().idx_;
979 }
980
981
982 int & MathCursor::idx()
983 {
984         return cursor().idx_;
985 }
986
987
988 int MathCursor::pos() const
989 {
990         return cursor().pos_;
991 }
992
993
994 int & MathCursor::pos()
995 {
996         return cursor().pos_;
997 }
998
999
1000 bool MathCursor::inMacroMode() const
1001 {
1002         return imacro_;
1003 }
1004
1005
1006 bool MathCursor::selection() const
1007 {
1008         return selection_;
1009 }
1010
1011
1012 void MathCursor::clearLastCode()
1013 {
1014         lastcode_ = LM_TC_MIN;
1015 }
1016
1017
1018 void MathCursor::setLastCode(MathTextCodes t)
1019 {
1020         lastcode_ = t;
1021 }
1022
1023
1024 MathTextCodes MathCursor::getLastCode() const
1025 {
1026         return lastcode_;
1027 }
1028
1029
1030 MathArrayInset * MathCursor::enclosingArray(int & idx) const
1031 {
1032         for (int i = Cursor_.size() - 1; i >= 0; --i) {
1033                 if (Cursor_[i].par_->isArray()) {
1034                         idx = Cursor_[i].idx_;
1035                         return static_cast<MathArrayInset *>(Cursor_[i].par_);
1036                 }
1037         }
1038         return 0;
1039 }
1040
1041
1042 void MathCursor::pullArg(bool goright)
1043 {
1044         // pullArg
1045         dump("pullarg");
1046         MathArray a = array();
1047         if (pop()) {
1048                 plainErase();
1049                 array().insert(pos(), a);
1050                 if (goright) 
1051                         pos() += a.size();
1052         }
1053 }
1054
1055
1056 MathStyles MathCursor::style() const
1057 {
1058         return xarray().style();
1059 }
1060
1061
1062 void MathCursor::normalize() const
1063 {
1064 #ifdef WITH_WARNINGS
1065 #warning This is evil!
1066 #endif
1067         MathCursor * it = const_cast<MathCursor *>(this);
1068
1069         if (idx() < 0 || idx() > par()->nargs() - 1)
1070                 lyxerr << "this should not really happen - 1\n";
1071         it->idx()    = max(idx(), 0);
1072         it->idx()    = min(idx(), par()->nargs() - 1);
1073
1074         if (pos() < 0 || pos() > array().size())
1075                 lyxerr << "this should not really happen - 2\n";
1076         it->pos() = max(pos(), 0);
1077         it->pos() = min(pos(), array().size());
1078 }
1079
1080
1081 int MathCursor::col() const
1082 {
1083         return par()->col(idx());
1084 }
1085
1086
1087 int MathCursor::row() const
1088 {
1089         return par()->row(idx());
1090 }
1091
1092
1093 /*
1094 char MathCursorPos::getChar() const
1095 {
1096         return array().getChar(pos());
1097 }
1098
1099
1100 string MathCursorPos::readString()
1101 {
1102         string s;
1103         int code = nextCode();
1104         for ( ; OK() && nextCode() == code; Next()) 
1105                 s += getChar();
1106
1107         return s;
1108 }
1109 */
1110
1111
1112 MathInset * MathCursor::prevInset() const
1113 {
1114         normalize();
1115         if (!pos())
1116                 return 0;
1117         return array().nextInset(pos() - 1);
1118 }
1119
1120
1121 MathInset * MathCursor::nextInset() const
1122 {
1123         normalize();
1124         return array().nextInset(pos());
1125 }
1126
1127
1128 MathScriptInset * MathCursor::prevScriptInset() const
1129 {
1130         normalize();
1131         MathInset * p = prevInset();
1132         return (p && p->isScriptInset()) ? static_cast<MathScriptInset *>(p) : 0;
1133 }
1134
1135
1136 MathSpaceInset * MathCursor::prevSpaceInset() const
1137 {
1138         normalize();
1139         MathInset * p = prevInset();
1140         return (p && p->isSpaceInset()) ? static_cast<MathSpaceInset *>(p) : 0;
1141 }
1142
1143
1144 MathArray & MathCursor::array() const
1145 {
1146         static MathArray dummy;
1147         if (!par()) {
1148                 lyxerr << "############  par_ not valid\n";
1149                 return dummy;
1150         }
1151
1152         if (idx() < 0 || idx() >= par()->nargs()) {
1153                 lyxerr << "############  idx_ " << idx() << " not valid\n";
1154                 return dummy;
1155         }
1156
1157         return cursor().cell();
1158 }
1159
1160
1161 MathXArray & MathCursor::xarray() const
1162 {
1163         return cursor().xcell();
1164 }
1165
1166
1167 int MathCursor::xpos() const 
1168 {
1169         normalize();
1170         return xarray().pos2x(pos());
1171 }
1172
1173
1174 void MathCursor::gotoX(int x)
1175 {
1176         pos() = xarray().x2pos(x);      
1177 }
1178
1179
1180 void MathCursor::idxNext()
1181 {
1182         par()->idxNext(idx(), pos());
1183 }
1184
1185
1186 void MathCursor::idxPrev()
1187 {
1188         par()->idxPrev(idx(), pos());
1189 }
1190
1191
1192 void MathCursor::splitCell()
1193 {
1194         if (idx() == par()->nargs() - 1) 
1195                 return;
1196         MathArray ar = array();
1197         ar.erase(0, pos());
1198         array().erase(pos(), array().size());
1199         ++idx();
1200         pos() = 0;
1201         array().insert(0, ar);
1202 }
1203
1204
1205 void MathCursor::breakLine()
1206 {
1207         MathMatrixInset * p = outerPar();
1208         if (p->getType() == LM_OT_SIMPLE || p->getType() == LM_OT_EQUATION) {
1209                 p->mutate(LM_OT_EQNARRAY);
1210                 p->addRow(0);
1211                 idx() = p->nrows();
1212                 pos() = 0;
1213         } else {
1214                 p->addRow(row());
1215
1216                 // split line
1217                 const int r = row();
1218                 for (int c = col() + 1; c < p->ncols(); ++c) {
1219                         const int i1 = p->index(r, c);
1220                         const int i2 = p->index(r + 1, c);      
1221                         lyxerr << "swapping cells " << i1 << " and " << i2 << "\n";
1222                         p->cell(i1).swap(p->cell(i2));
1223                 }
1224
1225                 // split cell
1226                 splitCell();
1227                 p->cell(idx()).swap(p->cell(idx() + p->ncols() - 1));
1228         }
1229 }
1230
1231
1232 char MathCursor::valign() const
1233 {
1234         int idx;
1235         MathArrayInset * p = enclosingArray(idx);
1236         return p ? p->valign() : 0;
1237 }
1238
1239
1240 char MathCursor::halign() const
1241 {
1242         int idx;
1243         MathArrayInset * p = enclosingArray(idx);
1244         return p ? p->halign(idx % p->ncols()) : 0;
1245 }
1246
1247
1248 MathCursorPos MathCursor::firstSelectionPos() const
1249 {
1250         MathCursorPos anc = normalAnchor();
1251         return anc < cursor() ? anc : cursor(); 
1252 }
1253
1254
1255 MathCursorPos MathCursor::lastSelectionPos() const
1256 {
1257         MathCursorPos anc = normalAnchor();
1258         return anc < cursor() ? cursor() : anc; 
1259 }
1260
1261
1262 void MathCursor::getSelection(MathCursorPos & i1, MathCursorPos & i2) const
1263 {
1264         MathCursorPos anc = normalAnchor();
1265         if (anc < cursor()) {
1266                 i1 = anc;
1267                 i2 = cursor();
1268         } else {
1269                 i1 = cursor();
1270                 i2 = anc;
1271         }
1272 }
1273
1274
1275 MathCursorPos & MathCursor::cursor()
1276 {
1277         return Cursor_.back();
1278 }
1279
1280
1281 MathCursorPos const & MathCursor::cursor() const
1282 {
1283         return Cursor_.back();
1284 }
1285
1286
1287
1288 ////////////////////////////////////////////////////////////////////////
1289
1290
1291 bool operator==(MathCursorPos const & ti, MathCursorPos const & it)
1292 {
1293         return ti.par_ == it.par_ && ti.idx_ == it.idx_ && ti.pos_ == it.pos_;
1294 }
1295
1296
1297 bool operator<(MathCursorPos const & ti, MathCursorPos const & it)
1298 {
1299         if (ti.par_ != it.par_) {
1300                 lyxerr << "can't compare cursor and anchor in different insets\n";
1301                 return true;
1302         }
1303         if (ti.idx_ != it.idx_)
1304                 return ti.idx_ < it.idx_;
1305         return ti.pos_ < it.pos_;
1306 }
1307
1308
1309 MathArray & MathCursorPos::cell(int idx) const
1310 {
1311         return par_->cell(idx);
1312 }
1313
1314 MathArray & MathCursorPos::cell() const
1315 {
1316         return par_->cell(idx_);
1317 }
1318
1319
1320 MathXArray & MathCursorPos::xcell(int idx) const
1321 {
1322         return par_->xcell(idx);
1323 }
1324
1325
1326 MathXArray & MathCursorPos::xcell() const
1327 {
1328         return par_->xcell(idx_);
1329 }
1330
1331
1332 MathCursorPos MathCursor::normalAnchor() const
1333 {
1334         // use Anchor on the same level as Cursor
1335         MathCursorPos normal = Anchor_[Cursor_.size() - 1];
1336         if (Cursor_.size() < Anchor_.size() && !(normal < cursor())) {
1337                 // anchor is behind cursor -> move anchor behind the inset
1338                 ++normal.pos_;
1339         }
1340         //lyxerr << "normalizing: from " << Anchor_[Anchor_.size() - 1] << " to "
1341         //      << normal << "\n";
1342         return normal;
1343 }
1344
1345
1346 bool MathCursorPos::idxUp()
1347 {
1348         return par_->idxUp(idx_, pos_);
1349 }
1350
1351
1352 bool MathCursorPos::idxDown()
1353 {
1354         return par_->idxDown(idx_, pos_);
1355 }
1356
1357
1358 bool MathCursorPos::idxLeft()
1359 {
1360         return par_->idxLeft(idx_, pos_);
1361 }
1362
1363
1364 bool MathCursorPos::idxRight()
1365 {
1366         return par_->idxRight(idx_, pos_);
1367 }
1368
1369
1370 MathMatrixInset * MathCursor::outerPar() const
1371 {
1372         return
1373                 static_cast<MathMatrixInset *>(const_cast<MathInset *>(formula_->par()));
1374 }