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