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