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