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