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