]> git.lyx.org Git - lyx.git/blob - src/mathed/math_cursor.C
c23a395021d02e15f273cb2f17d2111e61db64e4
[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 if necessary
419         if (cursor_ == 0 && array().size() == 0) {
420                 bool popit;
421                 bool removeit;
422                 par_->idxDelete(idx_, popit, removeit);
423                 if (popit && pop() && removeit)
424                         Delete();
425         }
426
427 #ifdef WITH_WARNINGS
428 #warning pullArg disabled
429 #endif
430         //if (cursor_ == 0 && !path_.empty()) {
431         //      lyxerr << "Delete: popping...\n";
432         //      pop();
433         //}
434
435         dump("Delete 2");
436 }
437
438
439 void MathCursor::DelLine()
440 {
441         MacroModeClose();
442
443         if (selection) {
444                 SelDel();
445                 return;
446         }
447
448         if (par_->nrows() > 1)
449                 par_->delRow(row());
450 }
451
452
453 bool MathCursor::Up(bool sel)
454 {
455         dump("Up 1");
456         MacroModeClose();
457         SelHandle(sel);
458         SelClear();
459
460         // check whether we could move into an inset on the right or on the left
461         MathInset * p = nextInset();
462         if (p) {
463                 int idx, cursor;
464                 if (p->idxFirstUp(idx, cursor)) {
465                         push(p, true);
466                         par_ = p;
467                         idx_    = idx;
468                         cursor_ = cursor;
469                         dump("Up 3");
470                         return true;
471                 }
472         }
473
474         p = prevInset();
475         if (p) {
476                 int idx, cursor;
477                 if (p->idxLastUp(idx, cursor)) {
478                         array().prev(cursor_);
479                         push(p, false);
480                         par_ = p;
481                         idx_    = idx;
482                         cursor_ = cursor;
483                         dump("Up 4");
484                         return true;
485                 }
486         }
487
488
489         int x = xarray().pos2x(cursor_);
490         bool result = par_->idxUp(idx_, cursor_);
491         if (!result && pop()) {
492                 result = par_->idxUp(idx_, cursor_);
493         }
494         cursor_ = xarray().x2pos(x);
495
496         dump("Up 2");
497         return result;
498 }
499
500
501 bool MathCursor::Down(bool sel)
502 {
503         dump("Down 1");
504         MacroModeClose();
505         SelHandle(sel);
506         SelClear();
507
508         // check whether we could move into an inset on the right or on the left
509         MathInset * p = nextInset();
510         if (p) {
511                 int idx, cursor;
512                 if (p->idxFirstDown(idx, cursor)) {
513                         push(p, true);
514                         idx_    = idx;
515                         cursor_ = cursor;
516                         dump("Down 3");
517                         return true;
518                 }
519         }
520
521         p = prevInset();
522         if (p) {
523                 int idx, cursor;
524                 if (p->idxLastDown(idx, cursor)) {
525                         array().prev(cursor_);
526                         push(p, false);
527                         idx_    = idx;
528                         cursor_ = cursor;
529                         dump("Down 4");
530                         return true;
531                 }
532         }
533
534         int x = xarray().pos2x(cursor_);
535         bool result = par_->idxDown(idx_, cursor_);
536         if (!result && pop()) {
537                 result = par_->idxDown(idx_, cursor_);
538         }
539         cursor_ = xarray().x2pos(x);
540
541         dump("Down 2");
542         return result;
543 }
544
545
546 bool MathCursor::toggleLimits()
547 {
548         if (!prevIsInset())
549                 return false;
550         MathInset * p = prevInset();
551         int old = p->limits();
552         p->limits(old == -1 ? 1 : -1);
553         return old != p->limits();
554 }
555
556
557 void MathCursor::SetSize(MathStyles size)
558 {
559         par_->UserSetSize(size);
560 }
561
562
563
564 void MathCursor::Interpret(string const & s)
565 {
566         lyxerr << "Interpret: '" << s << "'  ('" << s.substr(0, 7)  << "' " <<
567 in_word_set(s) << " \n";
568
569         if (s[0] == '^') {
570                 MathScriptInset * p = nearbyScriptInset();
571                 if (!p) {
572                         p = new MathScriptInset;
573                         insert(p);
574                         array().prev(cursor_);
575                 }
576                 push(p, true);
577                 if (!p->up())
578                         p->up(true);
579                 idx_ = 0;
580                 return;
581         }
582
583         if (s[0] == '_') {
584                 MathScriptInset * p = nearbyScriptInset();
585                 if (!p) {
586                         p = new MathScriptInset;
587                         insert(p);
588                         array().prev(cursor_);
589                 }
590                 push(p, true);
591                 if (!p->down())
592                         p->down(true);
593                 idx_ = 1;
594                 return;
595         }
596
597         if (s[0] == '!' || s[0] == ','  || s[0] == ':' || s[0] == ';') {
598                 int sp = (s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0));
599                 insert(new MathSpaceInset(sp));
600                 return;
601         }
602
603         MathInset * p = 0;
604         latexkeys const * l = in_word_set(s);
605
606         if (l == 0) {
607                 if (s == "root") 
608                         p = new MathRootInset;
609                 else if (MathMacroTable::hasTemplate(s))
610                         p = new MathMacro(MathMacroTable::provideTemplate(s));
611                 else if (s.size() > 7 && s.substr(0, 7) == "matrix ") {
612                         int m = 1;
613                         int n = 1;
614                         string v_align;
615                         string h_align;
616                         std::istringstream is(s.substr(7).c_str());
617                         is >> m >> n >> v_align >> h_align;
618                         m = std::max(1, m);
619                         n = std::max(1, n);
620                         MathArrayInset * pp = new MathArrayInset(m, n);
621                         pp->valign(v_align[0]);
622                         pp->halign(h_align);
623                         p = pp;
624                 }
625                 else
626                         p = new MathFuncInset(s, LM_OT_UNDEF);
627         } else {
628                 switch (l->token) {
629                         case LM_TK_BIGSYM: 
630                                         p = new MathBigopInset(l->name, l->id);
631                                         break;
632                                 
633                         case LM_TK_SYM: 
634                                 if (l->id < 255) {
635                                         insert(static_cast<byte>(l->id), 
636                                                MathIsBOPS(l->id) ?
637                                                 LM_TC_BOPS : LM_TC_SYMB);
638                                         
639                                 } else {
640                                         p = new MathFuncInset(l->name);
641                                 }
642                                 break;
643
644                         case LM_TK_STACK:
645                                 p = new MathFracInset("stackrel");
646                                 break;
647
648                         case LM_TK_FRAC:
649                                 p = new MathFracInset("frac");
650                                 break;
651
652                         case LM_TK_SQRT:
653                                 p = new MathSqrtInset;
654                                 break;
655
656                         case LM_TK_WIDE:
657                                 p = new MathDecorationInset(l->id);
658                                 break;
659
660                         case  LM_TK_FUNCLIM:
661                                 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
662                                 break;
663
664                         case LM_TK_SPACE:
665                                 p = new MathSpaceInset(l->id);
666                                 break;
667
668                         case LM_TK_DOTS:
669                                 p = new MathDotsInset(l->name, l->id);
670                                 break;
671
672                         case LM_TK_ACCENT:
673                                 setAccent(l->id);
674                                 break;
675
676                         case LM_TK_MACRO:
677                                 p = new MathMacro(MathMacroTable::provideTemplate(s));
678                                 break;
679
680                         default:
681                                 p = new MathFuncInset(l->name);
682                                 break;
683                 }
684         }
685
686         if (p) {
687                 bool oldsel = selection;
688                 if (oldsel) 
689                         SelCut();
690                 insert(p);
691                 if (p->nargs()) {
692                         array().prev(cursor_);
693                         push(p, true);
694                         if (oldsel) 
695                                 SelPaste();
696                 }
697                 p->Metrics(p->size());
698         }
699 }
700
701
702 void MathCursor::MacroModeOpen()
703 {
704         if (!macro_mode) {
705                 imacro = new MathFuncInset("");
706                 insert(imacro);
707                 macro_mode = true;
708         } else
709                 lyxerr << "Math Warning: Already in macro mode" << endl;
710 }
711
712
713 void MathCursor::MacroModeClose()
714 {
715         if (macro_mode)  {
716                 macro_mode = false;
717                 latexkeys const * l = in_word_set(imacro->name());
718                 if (!imacro->name().empty()
719                                 && (!l || (l && IsMacro(l->token, l->id)))
720                                 && !MathMacroTable::hasTemplate(imacro->name()))
721                 {
722                         if (!l) {
723                                 //imacro->SetName(macrobf);
724                                 // This guarantees that the string will be removed by destructor
725                                 imacro->SetType(LM_OT_UNDEF);
726                         } else
727                                 imacro->SetName(l->name);
728                 } else {
729                         Left();
730                         if (nextInset()->isAccentInset()) 
731                                 setAccent(
732                                         static_cast<MathAccentInset*>(nextInset())->getAccentCode());
733                         array().erase(cursor_);
734                         if (l || MathMacroTable::hasTemplate(imacro->name())) 
735                                 Interpret(imacro->name());
736                         imacro->SetName(string());
737                 }
738                 imacro = 0;
739         }
740 }
741
742
743 void MathCursor::SelCopy()
744 {
745         seldump("SelCopy");
746         if (selection) {
747                 int const p1 = min(cursor_, anchor_);
748                 int const p2 = max(cursor_, anchor_);
749                 selarray = array();
750                 selarray.erase(p2, selarray.size());
751                 selarray.erase(0, p1);
752                 SelClear();
753         }
754 }
755
756 void MathCursor::selArray(MathArray & ar) const
757 {
758         int const p1 = min(cursor_, anchor_);
759         int const p2 = max(cursor_, anchor_);
760         ar = array();
761         ar.erase(p2, ar.size());
762         ar.erase(0, p1);
763 }
764
765
766 void MathCursor::SelCut()
767 {
768         seldump("SelCut");
769         if (selection) {
770                 int const p1 = min(cursor_, anchor_);
771                 int const p2 = max(cursor_, anchor_);
772                 cursor_ = p1;  // move cursor to a same position
773                 selarray = array();
774                 selarray.erase(p2, selarray.size());
775                 selarray.erase(0, p1);
776                 array().erase(p1, p2);
777                 SelClear();
778         }
779 }
780
781
782 void MathCursor::SelDel()
783 {
784         seldump("SelDel");
785         if (selection) {
786                 int const p1 = min(cursor_, anchor_);
787                 int const p2 = max(cursor_, anchor_);
788                 array().erase(p1, p2);
789                 SelClear();
790         }
791 }
792
793
794 void MathCursor::SelPaste()
795 {
796         seldump("SelPaste");
797         array().insert(cursor_, selarray);
798         cursor_ += selarray.size();
799         SelClear();
800 }
801
802 void MathCursor::SelHandle(bool sel)
803 {
804         if (sel && !selection)
805                 SelStart();
806         if (!sel && selection)
807                 SelClear();
808 }
809
810
811 void MathCursor::SelStart()
812 {
813         seldump("SelStart");
814         if (selection)
815                 return;
816
817         anchor_   = cursor_;
818         selection = true;
819 }
820
821
822 void MathCursor::SelClear()
823 {
824         selection = false;
825 }
826
827
828
829 void MathCursor::SelGetArea(int * xpoint, int * ypoint, int & n)
830 {
831         if (!selection) {
832                 n = 0;
833                 xpoint[0] = 0;
834                 ypoint[0] = 0;
835                 return;
836         }
837
838         // Balance anchor and cursor
839         int xo;
840         int yo;
841         par()->GetXY(xo, yo);
842         int w = par()->width();
843         // cursor
844         int x1 = xarray().xo() + xarray().pos2x(cursor_);
845         int y1 = xarray().yo();
846         //int a1 = xarray().ascent();
847         //int d1 = xarray().descent();
848
849         // anchor
850         int x  = xarray().xo() + xarray().pos2x(anchor_);
851         int y  = xarray().yo();
852         int a  = xarray().ascent();
853         int d  = xarray().descent();
854
855         // single row selection
856         n = 0;
857         xpoint[n]   = x;
858         ypoint[n++] = y + d;
859         xpoint[n]   = x;
860         ypoint[n++] = y - a;
861
862         if (y != y1) {
863                 xpoint[n]   = xo + w;
864                 ypoint[n++] = y - a;
865
866                 if (x1 < xo + w) {
867                         xpoint[n]   = xo + w;
868                         ypoint[n++] = y1 - a;
869                 }
870         }
871
872         xpoint[n]   = x1;
873         ypoint[n++] = y1 - a;
874         xpoint[n]   = x1;
875         ypoint[n++] = y1 + d;
876
877         if (y != y1) {
878                 xpoint[n]   = xo;
879                 ypoint[n++] = y1 + d;
880                 if (x > xo) {
881                         xpoint[n]   = xo;
882                         ypoint[n++] = y + d;
883                 }
884         }
885         xpoint[n]   = xpoint[0];
886         ypoint[n++] = ypoint[0];
887
888         //lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "]\n";
889         //lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "]\n";
890         //for (i = 0; i < np; ++i)
891         //      lyxerr << "XY[" << xpoint[i] << " " << ypoint[i] << "]\n";
892 }
893
894
895 void MathCursor::setAccent(int ac)
896 {
897         if (ac > 0 && accent < 8)
898                 nestaccent[accent++] = ac;
899         else
900                 accent = 0;  // consumed!
901 }
902
903
904 int MathCursor::getAccent() const
905 {
906         return accent > 0 ? nestaccent[accent - 1] : 0;
907 }
908
909
910 void MathCursor::doAccent(char c, MathTextCodes t)
911 {
912         MathInset * ac = 0;
913
914         for (int i = accent - 1; i >= 0; --i) {
915                 if (i == accent - 1)
916                         ac = new MathAccentInset(c, t, nestaccent[i]);
917                 else
918                         ac = new MathAccentInset(ac, nestaccent[i]);
919         }
920         
921         if (ac)
922                 insert(ac);
923
924         accent = 0;  // consumed!
925 }
926
927
928 void MathCursor::doAccent(MathInset * p)
929 {
930         MathInset * ac = 0;
931
932         for (int i = accent - 1; i >= 0; --i) {
933                 if (i == accent - 1)
934                         ac = new MathAccentInset(p, nestaccent[i]);
935                 else
936                         ac = new MathAccentInset(ac, nestaccent[i]);
937         }
938
939         if (ac)
940                 insert(ac);
941
942         accent = 0;  // consumed!
943 }
944
945
946 void MathCursor::handleFont(MathTextCodes t)
947 {
948         if (selection)  {
949                 int const p1 = std::min(cursor_, anchor_);
950                 int const p2 = std::max(cursor_, anchor_);
951                 MathArray & ar = array();
952                 for (int pos = p1; pos != p2; ar.next(pos))
953                         if (!ar.isInset(pos) && isalnum(ar.GetChar(pos))) { 
954                                 MathTextCodes c = ar.GetCode(pos) == t ? LM_TC_VAR : t;
955                                 ar.setCode(pos, c);
956                         }
957         } else {
958                 if (lastcode == t)
959                         lastcode = LM_TC_VAR;
960                 else
961                         lastcode = t;
962         }
963 }
964
965
966 void MathCursor::GetPos(int & x, int & y)
967 {
968         x = xarray().xo() + xarray().pos2x(cursor_);
969         y = xarray().yo();
970 }
971
972
973 MathTextCodes MathCursor::nextCode() const
974 {
975         return array().GetCode(cursor_); 
976 }
977
978
979 MathTextCodes MathCursor::prevCode() const
980 {
981         return array().GetCode(cursor_ - 1); 
982 }
983
984
985 MathInset * MathCursor::par() const
986 {
987         return par_;
988 }
989
990
991 InsetFormulaBase const * MathCursor::formula()
992 {
993         return formula_;
994 }
995
996
997 int MathCursor::pos() const
998 {
999         return cursor_;
1000 }
1001
1002
1003 bool MathCursor::InMacroMode() const
1004 {
1005         return macro_mode;
1006 }
1007
1008
1009 bool MathCursor::Selection() const
1010 {
1011         return selection;
1012 }
1013
1014
1015 void MathCursor::clearLastCode()
1016 {
1017         lastcode = LM_TC_MIN;
1018 }
1019
1020
1021 void MathCursor::setLastCode(MathTextCodes t)
1022 {
1023         lastcode = t;
1024 }
1025
1026
1027 MathTextCodes MathCursor::getLastCode() const
1028 {
1029         return lastcode;
1030 }
1031
1032
1033 MathInset * MathCursor::enclosing(MathInsetTypes t, int & idx) const
1034 {
1035         if (par_->GetType() == t) {
1036                 //lyxerr << "enclosing par is current\n";
1037                 idx = idx_;
1038                 return par_;
1039         }
1040         for (int i = path_.size() - 1; i >= 0; --i) {
1041                 lyxerr << "checking level " << i << "\n";
1042                 if (path_[i].par_->GetType() == t) {
1043                         idx = path_[i].idx_;
1044                         return path_[i].par_;
1045                 }
1046         }
1047         return 0;
1048 }
1049
1050 void MathCursor::pullArg()
1051 {
1052         // pullArg
1053         MathArray a = array();
1054         if (!Left())
1055                 return;
1056         normalize();
1057         array().erase(cursor_);
1058         array().insert(cursor_, a);
1059 }
1060
1061
1062 MathStyles MathCursor::style() const
1063 {
1064         return xarray().style();
1065 }
1066
1067
1068 void MathCursor::normalize() const
1069 {
1070 #ifdef WITH_WARNINGS
1071 #warning This is evil!
1072 #endif
1073         MathCursor * it = const_cast<MathCursor *>(this);
1074
1075         if (idx_ < 0 || idx_ > par_->nargs())
1076                 lyxerr << "this should not really happen - 1\n";
1077         it->idx_    = max(idx_, 0);
1078         it->idx_    = min(idx_, par_->nargs());
1079
1080         if (cursor_ < 0 || cursor_ > array().size())
1081                 lyxerr << "this should not really happen - 2\n";
1082         it->cursor_ = max(cursor_, 0);
1083         it->cursor_ = min(cursor_, array().size());
1084 }
1085
1086
1087 int MathCursor::col() const
1088 {
1089         return par_->col(idx_);
1090 }
1091
1092
1093 int MathCursor::row() const
1094 {
1095         return par_->row(idx_);
1096 }
1097
1098
1099 /*
1100 char MathIter::GetChar() const
1101 {
1102         return array().GetChar(cursor_);
1103 }
1104
1105
1106 string MathIter::readString()
1107 {
1108         string s;
1109         int code = nextCode();
1110         for ( ; OK() && nextCode() == code; Next()) 
1111                 s += GetChar();
1112
1113         return s;
1114 }
1115 */
1116
1117 MathInset * MathCursor::prevInset() const
1118 {
1119         normalize();
1120         int c = cursor_;
1121         if (!array().prev(c))
1122                 return 0;
1123         return array().GetInset(c);
1124 }
1125
1126
1127 MathInset * MathCursor::nextInset() const
1128 {
1129         normalize();
1130         return array().GetInset(cursor_);
1131 }
1132
1133
1134 MathScriptInset * MathCursor::nearbyScriptInset() const
1135 {
1136         normalize();
1137         MathScriptInset * p = array().prevScriptInset(cursor_);
1138         if (p)
1139                 return p;
1140         return array().nextScriptInset(cursor_);
1141 }
1142
1143
1144
1145 MathArray & MathCursor::array() const
1146 {
1147         static MathArray dummy;
1148         if (!par_) {
1149                 lyxerr << "############  par_ not valid\n";
1150                 return dummy;
1151         }
1152
1153         if (idx_ < 0 || idx_ >= par_->nargs()) {
1154                 lyxerr << "############  idx_ " << idx_ << " not valid\n";
1155                 return dummy;
1156         }
1157
1158         return par_->cell(idx_);
1159 }
1160
1161
1162 MathXArray & MathCursor::xarray() const
1163 {
1164         return par_->xcell(idx_);
1165 }
1166
1167
1168
1169 bool MathCursor::nextIsInset() const
1170 {
1171         return cursor_ < array().size() && MathIsInset(nextCode());
1172 }
1173
1174
1175 bool MathCursor::prevIsInset() const
1176 {
1177         return cursor_ > 0 && MathIsInset(prevCode());
1178 }
1179
1180
1181 int MathCursor::xpos() const 
1182 {
1183         normalize();
1184         return xarray().pos2x(cursor_);
1185 }
1186
1187 void MathCursor::gotoX(int x)
1188 {
1189         cursor_ = xarray().x2pos(x);    
1190 }
1191
1192 void MathCursor::idxNext()
1193 {
1194         par_->idxNext(idx_, cursor_);
1195 }
1196
1197 void MathCursor::idxPrev()
1198 {
1199         par_->idxPrev(idx_, cursor_);
1200 }
1201
1202 void MathCursor::splitCell()
1203 {
1204         if (idx_ == par_->nargs() - 1) 
1205                 return;
1206         MathArray ar = array();
1207         ar.erase(0, cursor_);
1208         array().erase(cursor_, array().size());
1209         ++idx_;
1210         cursor_ = 0;
1211         array().insert(0, ar);
1212 }
1213
1214 void MathCursor::breakLine()
1215 {
1216         MathMatrixInset * p = static_cast<MathMatrixInset *>(formula()->par());
1217         if (p->GetType() == LM_OT_SIMPLE || p->GetType() == LM_OT_EQUATION) {
1218                 p->mutate(LM_OT_EQNARRAY);
1219                 p->addRow(row());
1220                 idx_ = p->nrows();
1221                 cursor_ = 0;
1222         } else {
1223                 p->addRow(row());
1224
1225                 // split line
1226                 const int r = row();
1227                 for (int c = col() + 1; c < p->ncols(); ++c) {
1228                         const int i1 = p->index(r, c);
1229                         const int i2 = p->index(r + 1, c);      
1230                         lyxerr << "swapping cells " << i1 << " and " << i2 << "\n";
1231                         p->cell(i1).swap(p->cell(i2));
1232                 }
1233
1234                 // split cell
1235                 splitCell();
1236                 p->cell(idx_).swap(p->cell(idx_ + p->ncols() - 1));
1237         }
1238 }
1239
1240 char MathCursor::valign() const
1241 {
1242         int idx;
1243         MathGridInset * p =
1244                 static_cast<MathGridInset *>(enclosing(LM_OT_MATRIX, idx));
1245         return p ? p->valign() : 0;
1246 }
1247
1248 char MathCursor::halign() const
1249 {
1250         int idx;
1251         MathGridInset * p =
1252                 static_cast<MathGridInset *>(enclosing(LM_OT_MATRIX, idx));
1253         return p ? p->halign(idx % p->ncols()) : 0;
1254 }