]> git.lyx.org Git - lyx.git/blob - src/mathed/math_cursor.C
mathed61.diff
[lyx.git] / src / mathed / math_cursor.C
1 /*
2 *  File:        math_cursor.C
3 *  Purpose:     Interaction for mathed
4 *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx>
5 *  Created:     January 1996
6 *  Description: Math interaction for a WYSIWYG math editor.
7 *
8 *  Dependencies: Xlib, XForms
9 *
10 *  Copyright: 1996, Alejandro Aguilar Sierra
11 *
12 *   Version: 0.8beta, Mathed & 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 FORMS_H_LOCATION
24 #include "math_inset.h"
25 #include "math_parser.h"
26 #include "math_cursor.h"
27 #include "math_macro.h"
28 #include "math_macrotable.h"
29 #include "math_root.h"
30 #include "support/lstrings.h"
31 #include "debug.h"
32 #include "LColor.h"
33 #include "Painter.h"
34 #include "math_matrixinset.h"
35 #include "math_rowst.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 "mathed/support.h"
45
46 static MathedArray selarray;
47
48 using std::cerr;
49 using std::endl;
50
51 // This was very smaller, I'll change it later
52 static inline
53 bool IsMacro(short tok, int id)
54 {
55         return tok != LM_TK_STACK &&
56                tok != LM_TK_FRAC &&
57                tok != LM_TK_SQRT &&
58                tok != LM_TK_WIDE &&
59                tok != LM_TK_SPACE &&
60                tok != LM_TK_DOTS &&
61                tok != LM_TK_FUNCLIM &&
62                tok != LM_TK_BIGSYM &&
63                tok != LM_TK_ACCENT &&
64                !(tok == LM_TK_SYM && id < 255);
65 }
66
67 static int const MAX_STACK_ITEMS = 32;
68
69 struct MathStackXIter {
70         std::vector<MathedXIter> item;
71         int i;
72
73         MathStackXIter(int n = MAX_STACK_ITEMS)
74                 : item(n), i(0) {
75         }
76
77         MathedXIter * push() {
78                 return &item[i++];
79         }
80
81         MathedXIter * pop() {
82     --i;
83                 return &item[i - 1];
84         }
85
86         MathedXIter * Item(int idx) {
87                 if (idx + 1 > i)
88                         cerr << "Wrong index: " << idx << " i: " << i << endl;
89                 return &item[i - idx - 1];
90         }
91
92         void Reset() {
93                 i = 0;
94         }
95
96         bool Full() {
97                 return i >= MAX_STACK_ITEMS;
98         }
99
100         bool Empty() {
101                 return i <= 1;
102         }
103
104         int Level() { return i; }
105
106 } mathstk, selstk;
107
108
109
110 /***----------------  Mathed Cursor  ---------------------------***/
111
112 MathedCursor::MathedCursor(MathParInset * p) // : par(p)
113 {
114         accent   = 0;
115         anchor   = 0;
116         lastcode = LM_TC_MIN;
117         SetPar(p);
118         if (!MathMacroTable::built)
119                 MathMacroTable::mathMTable.builtinMacros();
120 }
121
122
123 void MathedCursor::SetPar(MathParInset * p)
124 {
125         macro_mode = false;
126         selection  = false; // not SelClear() ?
127         mathstk.Reset();
128         cursor = mathstk.push();
129         par = p;
130         cursor->SetData(par);
131 }
132
133
134 void MathedCursor::draw(Painter & pain, int x, int y)
135 {
136         //    lyxerr << "Cursor[" << x << " " << y << "] ";
137         //win = pm;    // win = (mathedCanvas) ? mathedCanvas: pm;
138
139         par->Metrics();
140         int w = par->Width() + 2;
141         int a = par->Ascent() + 1;
142         int h = par->Height() + 1;
143
144         if (par->GetType() > LM_OT_PAR) {
145                 a += 4;
146                 h += 8;
147         }
148
149         pain.rectangle(x - 1, y - a, w, h, LColor::mathframe);
150
151         par->draw(pain, x, y);
152         cursor->Adjust();
153 }
154
155
156 void MathedCursor::Redraw(Painter & pain)
157 {
158         lyxerr[Debug::MATHED] << "Mathed: Redrawing!" << endl;
159         par->Metrics();
160         int w = par->Width();
161         int h = par->Height();
162         int x;
163         int y;
164         par->GetXY(x, y);
165         //mathed_set_font(LM_TC_VAR, 1);
166         pain.fillRectangle(x, y - par->Ascent(),
167         x + w, y - par->Ascent() + h,
168         LColor::mathbg);
169         par->draw(pain, x, y);
170 }
171
172
173 bool MathedCursor::Left(bool sel)
174 {
175         if (macro_mode) {
176                 // was MacroModeBack()
177                 if (!imacro->GetName().empty()) {
178                         imacro->SetName(imacro->GetName()
179                                         .substr(0, imacro->GetName()
180                                                 .length() - 1));
181                         imacro->Metrics();
182                 } else
183                         MacroModeClose();
184                 return true;
185         }
186
187         clearLastCode();
188
189         if (sel && !selection)
190                 SelStart();
191
192         if (!sel && selection)
193                 SelClear();
194
195         bool result = cursor->Prev();
196
197         if (!result && !mathstk.Empty()) {
198                 cursor = mathstk.pop();
199                 cursor->Adjust();
200                 result = true;
201                 if (selection)
202                         SelClear();
203         } else if (result && cursor->IsActive()) {
204                 if (cursor->IsScript()) {
205                         cursor->Prev();
206                         if (!cursor->IsScript())
207                                 cursor->Next();
208                         cursor->Adjust();
209                         return true;
210                 }
211
212                 if (!selection) {
213                         MathParInset * p = cursor->GetActiveInset();
214                         if (!p)
215                                 return result;
216
217                         p->setArgumentIdx(p->getMaxArgumentIdx());
218                         cursor = mathstk.push();
219                         cursor->SetData(p);
220                         cursor->GoLast();
221                 }
222         }
223         return result;
224 }
225
226
227 // Leave the inset
228 bool MathedCursor::Pop()
229 {
230         if (!mathstk.Empty()) {
231                 cursor = mathstk.pop();
232                 cursor->Next();
233                 return true;
234         }
235         return false;
236 }
237
238
239 // Go to the inset
240 bool MathedCursor::Push()
241 {
242         if (cursor->IsActive()) {
243                 MathParInset * p = cursor->GetActiveInset();
244                 if (!p)
245                         return false;
246                 cursor = mathstk.push();
247                 cursor->SetData(p);
248                 return true;
249         }
250         return false;
251 }
252
253
254 bool MathedCursor::Right(bool sel)
255 {
256         if (macro_mode) {
257                 MacroModeClose();
258                 return true;
259         }
260
261         clearLastCode();
262
263         if (sel && !selection)
264                 SelStart();
265
266         if (!sel && selection)
267                 SelClear();
268
269         bool result = false;
270
271         if (cursor->IsActive()) {
272                 if (cursor->IsScript()) {
273                         cursor->Next();
274                         // A script may be followed by another script
275                         if (cursor->IsScript())
276                                 cursor->Next();
277                         return true;
278                 }
279
280                 if (!selection) {
281                         MathParInset *p = cursor->GetActiveInset();
282                         if (!p) {
283                         lyxerr << "Math error: Inset expected." << endl;
284                         return cursor->Next();
285                         }
286                         p->setArgumentIdx(0);
287                         cursor = mathstk.push();
288                         cursor->SetData(p);
289                         result = true;
290                 } else
291                         result = cursor->Next();
292
293         } else {
294                 if (cursor->GetChar()!= LM_TC_CR)
295                 result = cursor->Next();
296                 if (!result && !mathstk.Empty()) {
297                         cursor = mathstk.pop();
298                         cursor->Next();
299                         cursor->Adjust();
300                         result = true;
301                         if (selection)
302                                 SelClear();
303                 }
304         }
305         return result;
306 }
307
308
309 void MathedCursor::SetPos(int x, int y)
310 {
311         int xp = 0;
312
313         if (macro_mode)
314                 MacroModeClose();
315
316         lastcode = LM_TC_MIN;
317         mathstk.Reset();
318         cursor = mathstk.push();
319         cursor->SetData(par);
320         cursor->fitCoord(x, y);
321
322         while (cursor->GetX()<x && cursor->OK()) {
323                 if (cursor->IsActive()) {
324                         MathParInset * p = cursor->GetActiveInset();
325                         if (p->Inside(x, y)) {
326                                 p->SetFocus(x, y);
327                                 cursor = mathstk.push();
328                                 cursor->SetData(p);
329                                 cursor->fitCoord(x, y);
330                                 continue;
331                         }
332                 }
333                 xp = cursor->GetX();
334                 cursor->ipush();
335                 if (!cursor->Next() && !Pop())
336                         break;
337         }
338         if (x - xp < cursor->GetX() - x)
339                 cursor->ipop();
340         cursor->Adjust();
341 }
342
343
344 void MathedCursor::Home()
345 {
346         if (macro_mode)
347                 MacroModeClose();
348         clearLastCode();
349         mathstk.Reset();
350         cursor = mathstk.push();
351         cursor->GoBegin();
352 }
353
354
355 void MathedCursor::End()
356 {
357         if (macro_mode)
358                 MacroModeClose();
359         clearLastCode();
360         mathstk.Reset();
361         cursor = mathstk.push();
362         cursor->GoLast();
363 }
364
365
366 MathMatrixInset * create_multiline(short int type, int cols)
367 {
368         int columns;
369         string align;
370         if (cols < 1)
371                 cols = 1;
372
373         switch (type) {
374                 case LM_OT_ALIGN:
375                 case LM_OT_ALIGNN:
376                         columns = 2*cols;
377                         for (int i = 0; i < cols; ++i)
378                         align += "Rl";
379                         break;
380
381                 case LM_OT_ALIGNAT:
382                 case LM_OT_ALIGNATN:
383                         columns = 2*cols;
384                         for (int i = 0; i < cols; ++i)
385                         align += "rl";
386                         break;
387
388                 case LM_OT_MULTLINE:
389                 case LM_OT_MULTLINEN:
390                         columns = 1;
391                         align = "C";
392                         break;
393
394                 case LM_OT_MPAR:
395                 case LM_OT_MPARN:
396                 default:
397                         columns = 3;
398                         align = "rcl";
399                         break;
400         }
401
402         MathMatrixInset * mt = new MathMatrixInset(columns, -1);
403         mt->SetAlign(' ', align);
404         return mt;
405 }
406
407
408 void MathedCursor::Insert(byte c, MathedTextCodes t)
409 {
410         if (selection)
411                 SelDel();
412
413         if (t == LM_TC_MIN)
414                 t = lastcode;
415
416         if (macro_mode && !(MathIsAlphaFont(t) || t == LM_TC_MIN))
417                 MacroModeClose();
418
419         if (t == LM_TC_CR) {
420                 MathParInset * p = cursor->getPar();
421                 if (p == par && p->GetType()<LM_OT_MPAR && p->GetType()>LM_OT_MIN) {
422                         short int type = LM_OT_MPAR;
423                         int cols = 1;
424                         if (c >= '1' && c <= '9') {
425                                 type = LM_OT_ALIGN;
426                                 cols = c - '1' + 1;
427                         } else if (c >= 'A' && c <= 'I') {
428                                 type = LM_OT_ALIGNAT;
429                                 cols = c - 'A' + 1;
430                         } else if (c == 'm')
431                                 type = LM_OT_MULTLINE;
432                         else if (c == 'e')
433                                 type = LM_OT_MPAR;
434
435                         if (p->GetType() == LM_OT_PARN)
436                                 ++type;
437                         MathMatrixInset * mt = create_multiline(type, cols);
438                         mt->SetStyle(LM_ST_DISPLAY);
439                         mt->SetType(type);
440                         mt->setData(p->GetData());
441                         delete p;
442                         par = mt;
443                         p = mt;
444                         p->Metrics();
445                         int pos = cursor->getPos();
446                         cursor->SetData(par);
447                         cursor->goPosAbs(pos);
448                 }
449                 if (p &&  p->Permit(LMPF_ALLOW_CR)) {
450                         cursor->addRow();
451                 }
452         } else if (t == LM_TC_TAB) {
453                 MathParInset * p = cursor->getPar();
454                 if (p &&  p->Permit(LMPF_ALLOW_TAB)) {
455                         if (c) {
456                                 cursor->insert(c, t);
457                                 cursor->checkTabs();
458                         } else
459                                 cursor->goNextColumn();
460                 } else // Navigate between arguments
461                 if (p && p->GetType() == LM_OT_MACRO) {
462                         if (p->getArgumentIdx() < p->getMaxArgumentIdx()) {
463                                 p->setArgumentIdx(p->getArgumentIdx() + 1);
464                                 cursor->SetData(p);
465                                 return;
466                         }
467                 }
468         } else {
469                 if (macro_mode) {
470                         if (MathIsAlphaFont(t) || t == LM_TC_MIN) {
471                                 // was MacroModeInsert(c);
472                                 imacro->SetName(imacro->GetName() + static_cast<char>(c));
473                                 return;
474                         }
475                 }
476
477                 if (accent) {
478                         doAccent(c, t);
479                 } else
480                         cursor->insert(c, t);
481
482                 lastcode = t;
483                 return;
484         }
485         clearLastCode();
486 }
487
488
489 void MathedCursor::insertInset(MathedInset * p, int t)
490 {
491         if (macro_mode)
492                 MacroModeClose();
493
494         if (selection) {
495                 if (MathIsActive(t)) {
496                         SelCut();
497                         static_cast<MathParInset*>(p)->setData(selarray);
498                 } else
499                         SelDel();
500         }
501
502         if (mathstk.i < MAX_STACK_ITEMS - 1) {
503                 if (accent && !MathIsActive(t)) {       
504                         doAccent(p);
505                 } else {
506                         cursor->insertInset(p, t);
507                         if (MathIsActive(t)) {
508                                 cursor->Prev();
509                                 Push();
510                         }
511                 }
512         } else
513                 lyxerr << "Math error: Full stack." << endl;
514 }
515
516
517 void MathedCursor::Delete()
518 {
519         if (macro_mode)
520                 return;
521
522         if (selection) {
523                 SelDel();
524                 return;
525         }
526
527         if (cursor->Empty() && !mathstk.Empty()) 
528                 cursor = mathstk.pop();
529
530         //   if (cursor->GetChar()!= LM_TC_TAB)
531         cursor->Delete();
532         cursor->checkTabs();
533 }
534
535
536 void MathedCursor::DelLine()
537 {
538         if (macro_mode)
539                 MacroModeClose();
540
541         if (selection) {
542                 SelDel();
543                 return;
544         }
545
546         MathParInset * p = cursor->getPar();
547
548         if (p && p->GetType() <= LM_OT_MATRIX && p->GetType() >= LM_OT_MPAR) {
549                 cursor->delRow();
550         }
551 }
552
553
554 bool MathedCursor::Up(bool sel)
555 {
556         bool result = false;
557
558         if (macro_mode)
559                 MacroModeClose();
560
561         if (sel && !selection)
562                 SelStart();
563
564         if (!sel && selection)
565                 SelClear();
566
567         if (cursor->IsScript()) {
568                 char cd = cursor->GetChar();
569                 if (MathIsUp(cd)) {
570                         Push();
571                         return true;
572                 } else {
573                         // A subscript may be followed by a superscript
574                         cursor->ipush();
575                         cursor->Next();
576                         if (MathIsUp(cursor->GetChar())) {
577                                 Push();
578                                 return true;
579                         } else  // return to the previous state
580                                 cursor->ipop();
581                 }
582         }
583
584         result = cursor->Up();
585         if (!result && cursor->getPar()) {
586                 MathParInset * p = cursor->getPar();
587
588                 if (p->GetType() == LM_OT_SCRIPT) {
589                         MathedXIter * cx = mathstk.Item(1);
590                         bool is_down = (cx->GetChar() == LM_TC_DOWN);
591                         cursor = mathstk.pop();
592                         cursor->Next();
593                         result =  (is_down) ? true: Up();
594                 } else {
595                         result = (p->getArgumentIdx() > 0);
596                         if (result) {
597                                 p->setArgumentIdx(p->getArgumentIdx() - 1);
598                                 cursor->SetData(p);
599                         }
600                 }
601
602                 if (!result && !mathstk.Empty()) {
603                         cursor = mathstk.pop();
604                         return Up();
605                 }
606         }
607         return result;
608 }
609
610
611 bool MathedCursor::Down(bool sel)
612 {
613         bool result = false;
614
615         if (macro_mode)
616                 MacroModeClose();
617
618         if (sel && !selection)
619                 SelStart();
620
621         if (!sel && selection)
622                 SelClear();
623
624         if (cursor->IsScript()) {
625                 char cd = cursor->GetChar();
626                 if (MathIsDown(cd)) {
627                         Push();
628                         return true;
629                 } else {
630                 // A superscript may be followed by a subscript
631                 cursor->ipush();
632                 cursor->Next();
633                 if (MathIsDown(cursor->GetChar())) {
634                         Push();
635                         return true;
636                 } else
637                         cursor->ipop();
638                 }
639         }
640
641         result = cursor->Down();
642         if (!result && cursor->getPar()) {
643                 MathParInset * p= cursor->getPar();
644                 if (p->GetType() == LM_OT_SCRIPT) {
645                         MathedXIter * cx = mathstk.Item(1);
646                         bool is_up = (cx->GetChar() == LM_TC_UP);
647                         cursor = mathstk.pop();
648                         cursor->Next();
649                         result = (is_up) ? true : Down();
650                 } else {
651                         result = (p->getArgumentIdx() < p->getMaxArgumentIdx());
652                         if (result) {
653                                 p->setArgumentIdx(p->getArgumentIdx() + 1);
654                                 cursor->SetData(p);
655                         }
656                 }
657                 if (!result && !mathstk.Empty()) {
658                         cursor = mathstk.pop();
659                         return Down(sel);
660                 }
661         }
662         return result;
663 }
664
665
666 bool MathedCursor::Limits()
667 {
668         if (cursor->IsInset()) {
669                 MathedInset * p = cursor->GetInset();
670                 bool ol = p->GetLimits();
671                 p->SetLimits(!ol);
672                 return (ol!= p->GetLimits());
673         }
674         return false;
675 }
676
677
678 void MathedCursor::SetSize(short size)
679 {
680         MathParInset * p = cursor->getPar();
681         p->UserSetSize(size);
682         cursor->SetData(p);
683 }
684
685
686 void MathedCursor::setLabel(string const & label)
687 {
688         // ugly hack and possible bug
689         if (!cursor->setLabel(label))
690                 lyxerr << "MathErr: Bad place to set labels." << endl;
691 }
692
693
694 void MathedCursor::setNumbered()
695 {
696         // another ugly hack
697         MathedRowContainer::iterator crow = cursor->currentRow();
698         if (crow)
699                 crow->setNumbered(!crow->isNumbered());
700 }
701
702
703 void MathedCursor::Interpret(string const & s)
704 {
705         MathedInset * p = 0;
706         latexkeys const * l = 0;
707         MathedTextCodes tcode = LM_TC_INSET;
708
709         if (s[0] == '^' || s[0] == '_') {
710                 char c = cursor->GetChar();
711                 if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
712                         Push();
713                         return;
714                 } else
715
716                 // A script may be followed by a script
717                 if (MathIsUp(c)  || MathIsDown(c)) {
718                         cursor->ipush();
719                         cursor->Next();
720                         c = cursor->GetChar();
721                         if (MathIsUp(c) && s[0] == '^' || MathIsDown(c) && s[0] == '_') {
722                                 Push();
723                                 return;
724                         } else
725                                 cursor->ipop();
726                 }
727                 p = new MathParInset(LM_ST_SCRIPT, "", LM_OT_SCRIPT);
728                 insertInset(p, (s[0] == '_') ? LM_TC_DOWN: LM_TC_UP);
729                 return;
730         } else
731         if (s[0] == '!' || s[0] == ','  || s[0] == ':' || s[0] == ';') {
732                 int sp = ((s[0] == ',') ? 1:((s[0] == ':') ? 2:((s[0] == ';') ? 3: 0)));
733                 p = new MathSpaceInset(sp);
734                 insertInset(p, LM_TC_INSET);
735                 return;
736         } else
737                 l = in_word_set(s);
738
739         if (!l) {
740                 p = MathMacroTable::mathMTable.createMacro(s);
741                 if (!p) {
742                 lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
743                 if (s == "root") {
744                         p = new MathRootInset;
745                         tcode = LM_TC_ACTIVE_INSET;
746                 } else
747                         p = new MathFuncInset(s, LM_OT_UNDEF);
748                 } else {
749                         tcode = static_cast<MathMacro*>(p)->getTCode();
750                         lyxerr[Debug::MATHED] << "Macro2 " << s << ' ' << tcode << endl;
751                 }
752         } else {
753                 MathedInsetTypes fractype = LM_OT_FRAC;
754                 switch (l->token) {
755                         case LM_TK_BIGSYM: 
756                                         p = new MathBigopInset(l->name, l->id);
757                                         break;
758                                 
759                         case LM_TK_SYM: 
760                                         if (l->id<255) {
761                                                 Insert(static_cast<byte>(l->id), MathIsBOPS(l->id) ?
762                                                 LM_TC_BOPS: LM_TC_SYMB);        
763                                         } else {
764                                                 p = new MathFuncInset(l->name);
765                                         }
766                                         break;
767                                 
768                         case LM_TK_STACK:
769                                 fractype = LM_OT_STACKREL;
770                                 lyxerr[Debug::MATHED] << " i:stackrel " << endl;
771
772                         case LM_TK_FRAC:
773                                 p = new MathFracInset(fractype);
774                                 tcode = LM_TC_ACTIVE_INSET;
775                                 break;
776
777                         case LM_TK_SQRT:
778                                 p = new MathSqrtInset;
779                                 tcode = LM_TC_ACTIVE_INSET;
780                                 break;
781
782                         case LM_TK_WIDE:
783                                 p = new MathDecorationInset(l->id);
784                                 tcode = LM_TC_ACTIVE_INSET;
785                                 break;
786
787                         case  LM_TK_FUNCLIM:
788                                 p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
789                                 break;
790
791                         case LM_TK_SPACE:
792                                 p = new MathSpaceInset(l->id);
793                                 break;
794
795                         case LM_TK_DOTS:
796                                 p = new MathDotsInset(l->name, l->id);
797                                 break;
798
799                         case LM_TK_ACCENT:
800                                 setAccent(l->id);
801                                 break;
802
803                         case LM_TK_MACRO:
804                                 p = MathMacroTable::mathMTable.createMacro(s);
805                                 tcode = static_cast<MathMacro*>(p)->getTCode();
806                                 lyxerr[Debug::MATHED] << "Macro " << s << ' ' << tcode << endl;
807                                 break;
808
809                         default:
810                                 p = new MathFuncInset(l->name);
811                                 break;
812                 }
813         }
814
815         if (p) {
816                 insertInset(p, tcode);
817                 par->Metrics();
818         }
819 }
820
821
822 bool MathedCursor::pullArg()
823 {
824         if (cursor->IsActive()) {
825                 MathParInset * p = cursor->GetActiveInset();
826                 if (!p) 
827                         return false;
828                 
829                 MathedArray a = p->GetData();
830                 p->clear();
831                 Delete();
832                 if (!a.empty()) {
833                         cursor->Merge(a);
834                         cursor->Adjust();
835                 }
836
837                 return true;
838         }
839         return false;
840 }
841
842
843 void MathedCursor::MacroModeOpen()
844 {
845         if (!macro_mode) {
846                 imacro = new MathFuncInset("");
847                 insertInset(imacro, LM_TC_INSET);
848                 macro_mode = true;
849         } else
850                 lyxerr << "Mathed Warning: Already in macro mode" << endl;
851 }
852
853
854 void MathedCursor::MacroModeClose()
855 {
856         if (macro_mode)  {
857                 macro_mode = false;
858                 latexkeys const * l = in_word_set(imacro->GetName());
859                 if (!imacro->GetName().empty()
860                 && (!l || (l && IsMacro(l->token, l->id))) &&
861                 !MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
862                         if (!l) {
863                                 //imacro->SetName(macrobf);
864                                 // This guarantees that the string will be removed by destructor
865                                 imacro->SetType(LM_OT_UNDEF);
866                         } else
867                                 imacro->SetName(l->name);
868                 } else {
869                         Left();
870                         if (cursor->GetInset()->GetType() == LM_OT_ACCENT) {
871                                 setAccent(
872                                         static_cast<MathAccentInset*>(cursor->GetInset())->getAccentCode());
873                         }
874                         cursor->Delete();
875                         if (l || MathMacroTable::mathMTable.createMacro(imacro->GetName())) {
876                                 Interpret(imacro->GetName());
877                         }
878                         imacro->SetName("");
879                 }
880                 imacro = 0;
881         }
882 }
883
884
885 void MathedCursor::SelCopy()
886 {
887         if (selection) {
888                 int const p1 = (cursor->getPos() < selpos) ?
889                         cursor->getPos() : selpos;
890                 int const p2 = (cursor->getPos() > selpos) ?
891                         cursor->getPos() : selpos;
892                 selarray = *(cursor->GetData());
893                 selarray.shrink(p1, p2);
894                 cursor->Adjust();
895                 SelClear();
896         }
897 }
898
899
900 void MathedCursor::SelCut()
901 {
902         if (selection) {
903                 if (cursor->getPos() == selpos)
904                         return;
905
906                 int const p1 = (cursor->getPos() < selpos) ?
907                         cursor->getPos() : selpos;
908                 int const p2 = (cursor->getPos() > selpos) ?
909                         cursor->getPos() : selpos;
910                 selarray = *(cursor->GetData());
911                 selarray.shrink(p1, p2);
912                 cursor->Clean(selpos);
913                 cursor->Adjust();
914                 SelClear();
915         }
916 }
917
918
919 void MathedCursor::SelDel()
920 {
921         //  lyxerr << "Deleting sel "
922         if (selection) {
923                 if (cursor->getPos() == selpos)
924                         return;
925                 cursor->Clean(selpos);
926                 cursor->Adjust();
927                 SelClear();
928         }
929 }
930
931
932 void MathedCursor::SelPaste()
933 {
934         // lyxerr << "paste " << selarray << " " << curor->pos;
935         if (selection)
936                 SelDel();
937
938         if (!selarray.empty()) {
939                 cursor->Merge(selarray);
940                 cursor->Adjust();
941         }
942 }
943
944
945 void MathedCursor::SelStart()
946 {
947         lyxerr[Debug::MATHED] << "Starting sel " << endl;
948         if (!anchor) {
949                 selpos = cursor->getPos();
950                 selstk = mathstk;
951                 anchor = selstk.Item(-1);
952                 anchor->SetData(cursor->getPar());
953                 anchor->GoBegin();
954                 anchor->goPosAbs(selpos);
955                 selection = true;
956         }
957 }
958
959
960 void MathedCursor::SelClear()
961 {
962         lyxerr[Debug::MATHED] << "Clearing sel " << endl;
963         selection = false;
964         anchor = 0;
965 }
966
967
968
969 // Anchor position must be at the same level that stack.
970 void MathedCursor::SelBalance()
971 {
972         int d = mathstk.Level() - selstk.Level();
973
974         // If unbalanced, balance them
975         while (d != 0) {
976                 if (d < 0) {
977                         // lyxerr << "b[" << mathstk.Level() << " " << selstk.Level
978                         //  << " " << anchor->GetX() << " " << cursor->GetX() << "]";
979                         anchor = selstk.pop();
980                         if (anchor->GetX() >= cursor->GetX())
981                         anchor->Next();
982                 } else {
983                         // lyxerr <<"a[" << mathstk.Level() << " " << selstk.Level() <<"]";
984                         Pop();
985                 }
986                 d = mathstk.Level() - selstk.Level();
987         }
988
989         // Once balanced the levels, check that they are at the same paragraph
990         selpos = anchor->getPos();
991 }
992
993
994 void MathedCursor::SelGetArea(int ** xp, int ** yp, int & np)
995 {
996         static int xpoint[10];
997         static int ypoint[10];
998
999         if (!selection) {
1000                 np = 0;
1001                 xpoint[0] = 0;
1002                 ypoint[0] = 0;
1003                 *xp = &xpoint[0];
1004                 *yp = &ypoint[0];
1005                 return;
1006         }
1007
1008         // Balance anchor and cursor
1009         SelBalance();
1010
1011         int xo;
1012         int yo;
1013         cursor->getPar()->GetXY(xo, yo);
1014         int w = cursor->getPar()->Width();
1015         int x1;
1016         int y1;
1017         cursor->GetPos(x1, y1);
1018         int a1;
1019         int d1;
1020         cursor->getAD(a1, d1);
1021         int x;
1022         int y;
1023         anchor->GetPos(x, y);
1024         int a;
1025         int d;
1026         anchor->getAD(a, d);
1027
1028         // single row selection
1029         int i = 0;
1030         xpoint[i]   = x;
1031         ypoint[i++] = y + d;
1032         xpoint[i]   = x;
1033         ypoint[i++] = y - a;
1034
1035         if (y != y1) {
1036                 xpoint[i]   = xo + w;
1037                 ypoint[i++] = y - a;
1038
1039                 if (x1 < xo + w) {
1040                 xpoint[i]   = xo + w;
1041                 ypoint[i++] = y1 - a;
1042         }
1043         }
1044
1045         xpoint[i]   = x1;
1046         ypoint[i++] = y1 - a;
1047         xpoint[i]   = x1;
1048         ypoint[i++] = y1 + d;
1049
1050         if (y != y1) {
1051                 xpoint[i]   = xo;
1052                 ypoint[i++] = y1 + d;
1053                 if (x > xo) {
1054                         xpoint[i]   = xo;
1055                         ypoint[i++] = y + d;
1056                 }
1057         }
1058         xpoint[i]   = xpoint[0];
1059         ypoint[i++] = ypoint[0];
1060
1061         *xp = &xpoint[0];
1062         *yp = &ypoint[0];
1063         np = i;
1064         //    lyxerr << "AN[" << x << " " << y << " " << x1 << " " << y1 << "] ";
1065         //    lyxerr << "MT[" << a << " " << d << " " << a1 << " " << d1 << "] ";
1066         //    for (i = 0; i < np; ++i)
1067         //      lyxerr << "XY[" << point[i].x << " " << point[i].y << "] ";
1068 }
1069
1070
1071 void MathedCursor::setAccent(int ac)
1072 {
1073         if (ac > 0 && accent < 8) {
1074                 nestaccent[accent++] = ac;
1075         } else
1076                 accent = 0;  // consumed!
1077 }
1078
1079
1080 int MathedCursor::getAccent() const
1081 {
1082         return (accent > 0) ? nestaccent[accent - 1]: 0;
1083 }
1084
1085
1086 void MathedCursor::doAccent(byte c, MathedTextCodes t)
1087 {
1088         MathedInset * ac = 0;
1089
1090         for (int i = accent - 1; i >= 0; --i) {
1091                 if (i == accent - 1)
1092                         ac = new MathAccentInset(c, t, nestaccent[i]);
1093                 else
1094                         ac = new MathAccentInset(ac, nestaccent[i]);
1095         }
1096         
1097         if (ac)
1098                 cursor->insertInset(ac, LM_TC_INSET);
1099
1100         accent = 0;  // consumed!
1101 }
1102
1103
1104 void MathedCursor::doAccent(MathedInset * p)
1105 {
1106         MathedInset * ac = 0;
1107
1108         for (int i = accent - 1; i >= 0; --i) {
1109                 if (i == accent - 1)
1110                         ac = new MathAccentInset(p, nestaccent[i]);
1111                 else
1112                         ac = new MathAccentInset(ac, nestaccent[i]);
1113         }
1114
1115         if (ac)
1116                 cursor->insertInset(ac, LM_TC_INSET);
1117
1118         accent = 0;  // consumed!
1119 }
1120
1121
1122 void MathedCursor::toggleLastCode(MathedTextCodes t)
1123 {
1124         if (lastcode == t)
1125                 lastcode = LM_TC_VAR;
1126         else
1127                 lastcode = t;
1128 }
1129
1130
1131 void MathedCursor::GetPos(int & x, int & y)
1132 {
1133         cursor->GetPos(x, y);
1134 }
1135
1136
1137 short MathedCursor::GetFCode()
1138 {
1139         return cursor->fcode();
1140 }
1141
1142
1143 MathParInset * MathedCursor::GetPar()
1144 {
1145         return par;
1146 }
1147
1148
1149 MathParInset * MathedCursor::getCurrentPar() const
1150 {
1151         return cursor->getPar();
1152 }
1153
1154
1155 string const & MathedCursor::getLabel() const
1156 {
1157         return cursor->getLabel();
1158 }
1159
1160
1161 bool MathedCursor::IsEnd() const
1162 {
1163         return !cursor->OK();
1164 }
1165
1166
1167 bool MathedCursor::InMacroMode()
1168 {
1169         return macro_mode;
1170 }
1171
1172
1173 bool MathedCursor::Selection()
1174 {
1175         return selection;
1176 }
1177
1178
1179 void MathedCursor::clearLastCode()
1180 {
1181         lastcode = LM_TC_MIN;
1182 }
1183
1184
1185 void MathedCursor::setLastCode(MathedTextCodes t)
1186 {
1187         lastcode = t;
1188 }
1189
1190
1191 MathedTextCodes MathedCursor::getLastCode() const
1192 {
1193         return lastcode;
1194 }