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