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