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