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