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