]> git.lyx.org Git - lyx.git/blob - src/mathed/math_cursor.C
Edwin's "about" patch + consistent use of Lsstream.h
[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                         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(l->id, MathIsBOPS(l->id) ? LM_TC_BOPS : LM_TC_SYMB);
619                                 else
620                                         p = new MathFuncInset(l->name);
621                                 break;
622
623                         case LM_TK_STACK:
624                                 p = new MathFracInset("stackrel");
625                                 break;
626
627                         case LM_TK_FRAC:
628                                 p = new MathFracInset("frac");
629                                 break;
630
631                         case LM_TK_SQRT:
632                                 p = new MathSqrtInset;
633                                 break;
634
635                         case LM_TK_DECORATION:
636                                 p = new MathDecorationInset(l->name, l->id);
637                                 break;
638
639                         case  LM_TK_FUNCLIM:
640                                 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
641                                 break;
642
643                         case LM_TK_SPACE:
644                                 p = new MathSpaceInset(l->id);
645                                 break;
646
647                         case LM_TK_DOTS:
648                                 p = new MathDotsInset(l->name, l->id);
649                                 break;
650
651                         case LM_TK_MACRO:
652                                 p = new MathMacro(MathMacroTable::provideTemplate(s));
653                                 break;
654
655                         default:
656                                 p = new MathFuncInset(l->name);
657                                 break;
658                 }
659         }
660
661         if (p) {
662                 bool oldsel = selection;
663                 if (oldsel) 
664                         SelCut();
665                 insert(p);
666                 if (p->nargs()) {
667                         array().prev(cursor_);
668                         push(p, true);
669                         if (oldsel) 
670                                 SelPaste();
671                 }
672                 p->Metrics(p->size());
673         }
674 }
675
676
677 void MathCursor::MacroModeOpen()
678 {
679         if (!macro_mode) {
680                 imacro = new MathFuncInset("");
681                 insert(imacro);
682                 macro_mode = true;
683         } else
684                 lyxerr << "Math Warning: Already in macro mode" << endl;
685 }
686
687
688 void MathCursor::MacroModeClose()
689 {
690         if (macro_mode)  {
691                 macro_mode = false;
692                 latexkeys const * l = in_word_set(imacro->name());
693                 if (!imacro->name().empty()
694                                 && (!l || (l && IsMacro(l->token, l->id)))
695                                 && !MathMacroTable::hasTemplate(imacro->name()))
696                 {
697                         if (!l) {
698                                 //imacro->SetName(macrobf);
699                                 // This guarantees that the string will be removed by destructor
700                                 imacro->SetType(LM_OT_UNDEF);
701                         } else
702                                 imacro->SetName(l->name);
703                 } else {
704                         Left();
705                         array().erase(cursor_);
706                         if (l || MathMacroTable::hasTemplate(imacro->name())) 
707                                 Interpret(imacro->name());
708                         imacro->SetName(string());
709                 }
710                 imacro = 0;
711         }
712 }
713
714
715 void MathCursor::SelCopy()
716 {
717         seldump("SelCopy");
718         if (selection) {
719                 int const p1 = min(cursor_, anchor_);
720                 int const p2 = max(cursor_, anchor_);
721                 selarray = array();
722                 selarray.erase(p2, selarray.size());
723                 selarray.erase(0, p1);
724                 SelClear();
725         }
726 }
727
728 void MathCursor::SelCut()
729 {
730         seldump("SelCut");
731         if (selection) {
732                 int const p1 = min(cursor_, anchor_);
733                 int const p2 = max(cursor_, anchor_);
734                 cursor_ = p1;  // move cursor to a same position
735                 selarray = array();
736                 selarray.erase(p2, selarray.size());
737                 selarray.erase(0, p1);
738                 array().erase(p1, p2);
739                 SelClear();
740         }
741 }
742
743
744 void MathCursor::SelDel()
745 {
746         seldump("SelDel");
747         if (selection) {
748                 int const p1 = min(cursor_, anchor_);
749                 int const p2 = max(cursor_, anchor_);
750                 array().erase(p1, p2);
751                 SelClear();
752         }
753 }
754
755
756 void MathCursor::SelPaste()
757 {
758         seldump("SelPaste");
759         array().insert(cursor_, selarray);
760         cursor_ += selarray.size();
761         SelClear();
762 }
763
764 void MathCursor::SelHandle(bool sel)
765 {
766         if (sel && !selection)
767                 SelStart();
768         if (!sel && selection)
769                 SelClear();
770 }
771
772
773 void MathCursor::SelStart()
774 {
775         seldump("SelStart");
776         if (selection)
777                 return;
778
779         anchor_   = cursor_;
780         selection = true;
781 }
782
783
784 void MathCursor::SelClear()
785 {
786         selection = false;
787 }
788
789
790
791 void MathCursor::SelGetArea(int * xpoint, int * ypoint, int & n)
792 {
793         if (!selection) {
794                 n = 0;
795                 xpoint[0] = 0;
796                 ypoint[0] = 0;
797                 return;
798         }
799
800         // Balance anchor and cursor
801         int xo;
802         int yo;
803         par()->GetXY(xo, yo);
804         int w = par()->width();
805         // cursor
806         int x1 = xarray().xo() + xarray().pos2x(cursor_);
807         int y1 = xarray().yo();
808         //int a1 = xarray().ascent();
809         //int d1 = xarray().descent();
810
811         // anchor
812         int x  = xarray().xo() + xarray().pos2x(anchor_);
813         int y  = xarray().yo();
814         int a  = xarray().ascent();
815         int d  = xarray().descent();
816
817         // single row selection
818         n = 0;
819         xpoint[n]   = x;
820         ypoint[n++] = y + d;
821         xpoint[n]   = x;
822         ypoint[n++] = y - a;
823
824         if (y != y1) {
825                 xpoint[n]   = xo + w;
826                 ypoint[n++] = y - a;
827
828                 if (x1 < xo + w) {
829                         xpoint[n]   = xo + w;
830                         ypoint[n++] = y1 - a;
831                 }
832         }
833
834         xpoint[n]   = x1;
835         ypoint[n++] = y1 - a;
836         xpoint[n]   = x1;
837         ypoint[n++] = y1 + d;
838
839         if (y != y1) {
840                 xpoint[n]   = xo;
841                 ypoint[n++] = y1 + d;
842                 if (x > xo) {
843                         xpoint[n]   = xo;
844                         ypoint[n++] = y + d;
845                 }
846         }
847         xpoint[n]   = xpoint[0];
848         ypoint[n++] = ypoint[0];
849
850         //lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "]\n";
851         //lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "]\n";
852         //for (i = 0; i < np; ++i)
853         //      lyxerr << "XY[" << xpoint[i] << " " << ypoint[i] << "]\n";
854 }
855
856
857 void MathCursor::handleFont(MathTextCodes t)
858 {
859         if (selection)  {
860                 int const p1 = std::min(cursor_, anchor_);
861                 int const p2 = std::max(cursor_, anchor_);
862                 MathArray & ar = array();
863                 for (int pos = p1; pos != p2; ar.next(pos))
864                         if (!ar.isInset(pos) && isalnum(ar.GetChar(pos))) { 
865                                 MathTextCodes c = ar.GetCode(pos) == t ? LM_TC_VAR : t;
866                                 ar.setCode(pos, c);
867                         }
868         } else {
869                 if (lastcode == t)
870                         lastcode = LM_TC_VAR;
871                 else
872                         lastcode = t;
873         }
874 }
875
876
877 void MathCursor::handleAccent(string const & name, int code)
878 {
879         MathDecorationInset * p = new MathDecorationInset(name, code);
880         if (selection) {
881                 SelCut();
882                 p->cell(0) = selarray;
883         }
884         insert(p);
885         push(p, true);
886 }
887
888 void MathCursor::handleDelim(int l, int r)
889 {
890         MathDelimInset * p = new MathDelimInset(l, r);
891         if (selection) {
892                 SelCut();
893                 p->cell(0) = selarray;
894         }
895         insert(p);
896         push(p, true);
897 }
898
899
900 void MathCursor::GetPos(int & x, int & y)
901 {
902         x = xarray().xo() + xarray().pos2x(cursor_);
903         y = xarray().yo();
904 }
905
906
907 MathTextCodes MathCursor::nextCode() const
908 {
909         return array().GetCode(cursor_); 
910 }
911
912
913 MathTextCodes MathCursor::prevCode() const
914 {
915         return array().GetCode(cursor_ - 1); 
916 }
917
918
919 MathInset * MathCursor::par() const
920 {
921         return par_;
922 }
923
924
925 InsetFormulaBase const * MathCursor::formula()
926 {
927         return formula_;
928 }
929
930
931 int MathCursor::pos() const
932 {
933         return cursor_;
934 }
935
936
937 bool MathCursor::InMacroMode() const
938 {
939         return macro_mode;
940 }
941
942
943 bool MathCursor::Selection() const
944 {
945         return selection;
946 }
947
948
949 void MathCursor::clearLastCode()
950 {
951         lastcode = LM_TC_MIN;
952 }
953
954
955 void MathCursor::setLastCode(MathTextCodes t)
956 {
957         lastcode = t;
958 }
959
960
961 MathTextCodes MathCursor::getLastCode() const
962 {
963         return lastcode;
964 }
965
966
967 MathInset * MathCursor::enclosing(MathInsetTypes t, int & idx) const
968 {
969         if (par_->GetType() == t) {
970                 //lyxerr << "enclosing par is current\n";
971                 idx = idx_;
972                 return par_;
973         }
974         for (int i = path_.size() - 1; i >= 0; --i) {
975                 lyxerr << "checking level " << i << "\n";
976                 if (path_[i].par_->GetType() == t) {
977                         idx = path_[i].idx_;
978                         return path_[i].par_;
979                 }
980         }
981         return 0;
982 }
983
984 void MathCursor::pullArg()
985 {
986         // pullArg
987         MathArray a = array();
988         if (!Left())
989                 return;
990         normalize();
991         array().erase(cursor_);
992         array().insert(cursor_, a);
993 }
994
995
996 MathStyles MathCursor::style() const
997 {
998         return xarray().style();
999 }
1000
1001
1002 void MathCursor::normalize() const
1003 {
1004 #ifdef WITH_WARNINGS
1005 #warning This is evil!
1006 #endif
1007         MathCursor * it = const_cast<MathCursor *>(this);
1008
1009         if (idx_ < 0 || idx_ > par_->nargs())
1010                 lyxerr << "this should not really happen - 1\n";
1011         it->idx_    = max(idx_, 0);
1012         it->idx_    = min(idx_, par_->nargs());
1013
1014         if (cursor_ < 0 || cursor_ > array().size())
1015                 lyxerr << "this should not really happen - 2\n";
1016         it->cursor_ = max(cursor_, 0);
1017         it->cursor_ = min(cursor_, array().size());
1018 }
1019
1020
1021 int MathCursor::col() const
1022 {
1023         return par_->col(idx_);
1024 }
1025
1026
1027 int MathCursor::row() const
1028 {
1029         return par_->row(idx_);
1030 }
1031
1032
1033 /*
1034 char MathIter::GetChar() const
1035 {
1036         return array().GetChar(cursor_);
1037 }
1038
1039
1040 string MathIter::readString()
1041 {
1042         string s;
1043         int code = nextCode();
1044         for ( ; OK() && nextCode() == code; Next()) 
1045                 s += GetChar();
1046
1047         return s;
1048 }
1049 */
1050
1051 MathInset * MathCursor::prevInset() const
1052 {
1053         normalize();
1054         int c = cursor_;
1055         if (!array().prev(c))
1056                 return 0;
1057         return array().nextInset(c);
1058 }
1059
1060
1061 MathInset * MathCursor::nextInset() const
1062 {
1063         normalize();
1064         return array().nextInset(cursor_);
1065 }
1066
1067
1068 MathUpDownInset * MathCursor::nearbyUpDownInset() const
1069 {
1070         normalize();
1071         MathInset * p = array().prevInset(cursor_);
1072         if (p && p->isUpDownInset())
1073                 return static_cast<MathUpDownInset *>(p);
1074         p = array().nextInset(cursor_);
1075         if (p && p->isUpDownInset())
1076                 return static_cast<MathUpDownInset *>(p);
1077         return 0;
1078 }
1079
1080
1081 MathArray & MathCursor::array() const
1082 {
1083         static MathArray dummy;
1084         if (!par_) {
1085                 lyxerr << "############  par_ not valid\n";
1086                 return dummy;
1087         }
1088
1089         if (idx_ < 0 || idx_ >= par_->nargs()) {
1090                 lyxerr << "############  idx_ " << idx_ << " not valid\n";
1091                 return dummy;
1092         }
1093
1094         return par_->cell(idx_);
1095 }
1096
1097
1098 MathXArray & MathCursor::xarray() const
1099 {
1100         return par_->xcell(idx_);
1101 }
1102
1103
1104
1105 bool MathCursor::nextIsInset() const
1106 {
1107         return cursor_ < array().size() && MathIsInset(nextCode());
1108 }
1109
1110
1111 bool MathCursor::prevIsInset() const
1112 {
1113         return cursor_ > 0 && MathIsInset(prevCode());
1114 }
1115
1116
1117 int MathCursor::xpos() const 
1118 {
1119         normalize();
1120         return xarray().pos2x(cursor_);
1121 }
1122
1123 void MathCursor::gotoX(int x)
1124 {
1125         cursor_ = xarray().x2pos(x);    
1126 }
1127
1128 void MathCursor::idxNext()
1129 {
1130         par_->idxNext(idx_, cursor_);
1131 }
1132
1133 void MathCursor::idxPrev()
1134 {
1135         par_->idxPrev(idx_, cursor_);
1136 }
1137
1138 void MathCursor::splitCell()
1139 {
1140         if (idx_ == par_->nargs() - 1) 
1141                 return;
1142         MathArray ar = array();
1143         ar.erase(0, cursor_);
1144         array().erase(cursor_, array().size());
1145         ++idx_;
1146         cursor_ = 0;
1147         array().insert(0, ar);
1148 }
1149
1150 void MathCursor::breakLine()
1151 {
1152         MathMatrixInset * p = static_cast<MathMatrixInset *>(formula()->par());
1153         if (p->GetType() == LM_OT_SIMPLE || p->GetType() == LM_OT_EQUATION) {
1154                 p->mutate(LM_OT_EQNARRAY);
1155                 p->addRow(row());
1156                 idx_ = p->nrows();
1157                 cursor_ = 0;
1158         } else {
1159                 p->addRow(row());
1160
1161                 // split line
1162                 const int r = row();
1163                 for (int c = col() + 1; c < p->ncols(); ++c) {
1164                         const int i1 = p->index(r, c);
1165                         const int i2 = p->index(r + 1, c);      
1166                         lyxerr << "swapping cells " << i1 << " and " << i2 << "\n";
1167                         p->cell(i1).swap(p->cell(i2));
1168                 }
1169
1170                 // split cell
1171                 splitCell();
1172                 p->cell(idx_).swap(p->cell(idx_ + p->ncols() - 1));
1173         }
1174 }
1175
1176 char MathCursor::valign() const
1177 {
1178         int idx;
1179         MathGridInset * p =
1180                 static_cast<MathGridInset *>(enclosing(LM_OT_MATRIX, idx));
1181         return p ? p->valign() : 0;
1182 }
1183
1184 char MathCursor::halign() const
1185 {
1186         int idx;
1187         MathGridInset * p =
1188                 static_cast<MathGridInset *>(enclosing(LM_OT_MATRIX, idx));
1189         return p ? p->halign(idx % p->ncols()) : 0;
1190 }