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