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