]> git.lyx.org Git - lyx.git/blob - src/mathed/math_cursor.C
merge from the string-switch branch and ready for a prelease.
[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 "error.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 //    fprintf(stderr, "Cursor[%d %d] ", 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: Redrawing!", Error::MATHED);
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               fprintf(stderr, "Math error: Inset expected.\n");
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      fprintf(stderr, "Math error: Full stack.\n");
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       fprintf(stderr, "MathErr: Bad place to set labels.");
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(string("Macro2 ") + s + ' ' 
638                          + tostr(tcode), Error::MATHED);
639             if (strcmp("root", s)==0) {
640                 p = new MathRootInset();
641                 tcode = LM_TC_ACTIVE_INSET;
642             } else
643               p = new MathFuncInset(s, LM_OT_UNDEF);
644         } else {
645             tcode = ((MathMacro*)p)->getTCode();
646             fprintf(stderr, "Macro2 %s %d  ", s, tcode);
647         }
648     } else {
649         MathedInsetTypes fractype = LM_OT_FRAC;
650         switch (l->token) {
651          case LM_TK_BIGSYM:
652          {
653              p = new MathBigopInset(l->name, l->id);
654              break;
655          }
656          case LM_TK_SYM:
657          {                   
658              if (l->id<255) {
659                  Insert((byte)l->id, MathIsBOPS(l->id) ? 
660                         LM_TC_BOPS: LM_TC_SYMB);            
661              } else {
662                  p = new MathFuncInset(l->name);
663              }
664              break;
665          }  
666          case LM_TK_STACK:
667             fractype = LM_OT_STACKREL;
668             lyxerr.debug(" i:stackrel ", Error::MATHED);
669          case LM_TK_FRAC: 
670          {       
671              p = new MathFracInset(fractype);
672              tcode = LM_TC_ACTIVE_INSET;
673              break;
674          }
675          case LM_TK_SQRT: 
676          {       
677              p = new MathSqrtInset; 
678              tcode = LM_TC_ACTIVE_INSET;
679              break;
680          }
681          case LM_TK_WIDE: 
682          {       
683              p = new MathDecorationInset(l->id); 
684              tcode = LM_TC_ACTIVE_INSET;
685              break;
686          } 
687          case  LM_TK_FUNCLIM:
688          {
689              p = new MathFuncInset(l->name, LM_OT_FUNCLIM);
690              break;
691          }
692          case LM_TK_SPACE:
693          {
694              p = new MathSpaceInset(l->id);
695              break;
696          }         
697          case LM_TK_DOTS: 
698          {
699              p = new MathDotsInset(l->name, l->id);
700              break;
701          }         
702          case LM_TK_ACCENT:
703             setAccent(l->id);
704             break;
705          case LM_TK_MACRO:
706             p = MathMacroTable::mathMTable.getMacro(s);
707             tcode = ((MathMacro*)p)->getTCode();
708             lyxerr.debug(string("Macro ") + s + ' ' 
709                          + tostr(tcode), Error::MATHED);
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       macroln = 0;
750       macrobf[0] = '\0';
751       imacro = new MathFuncInset(&macrobf[0]);
752       Insert (imacro);
753       macro_mode = true;
754    } else
755      fprintf(stderr, "Mathed Warning: Already in macro mode\n");
756 }
757
758 void MathedCursor::MacroModeClose()
759 {
760    if (macro_mode)  {
761       macro_mode = false;
762       latexkeys *l = in_word_set(macrobf, macroln);
763       if (macroln>0 && (!l || (l && IsMacro(l->token, l->id))) && 
764           !MathMacroTable::mathMTable.getMacro(macrobf)) {
765           if (!l) {
766             imacro->SetName(strnew(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          imacro->SetName(0);
774          if (cursor->GetInset()->GetType()==LM_OT_ACCENT) {
775              setAccent(((MathAccentInset*)cursor->GetInset())->getAccentCode());
776          }
777          cursor->Delete();
778          if (l || MathMacroTable::mathMTable.getMacro(macrobf)) {
779             Interpret(macrobf);
780          }
781       }
782       imacro = 0;
783    }  
784 }
785
786 void MathedCursor::MacroModeBack()
787 {
788    if (macro_mode) {
789      if (macroln>0) {
790         macrobf[--macroln] = '\0';
791         imacro->Metrics();
792      } else 
793         MacroModeClose();
794    } else
795      fprintf(stderr, "Mathed Warning: we are not in macro mode\n");
796 }
797
798 void MathedCursor::MacroModeInsert(char c)
799 {
800    if (macro_mode) {
801       macrobf[macroln+1] = macrobf[macroln];
802       macrobf[macroln++] = c;
803       imacro->Metrics();
804    } else
805      fprintf(stderr, "Mathed Warning: we are not in macro mode\n");
806 }
807
808 void MathedCursor::SelCopy()
809 {
810     if (selection) {
811         int p1, p2;
812         p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
813         p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
814         selarray = cursor->Copy(p1, p2);
815         cursor->Adjust();
816         SelClear();
817     }
818 }
819
820 void MathedCursor::SelCut()
821 {   
822     if (selection) {
823         if (cursor->pos==selpos) return;
824         
825         int p1, p2;
826         p1 = (cursor->pos < selpos) ? cursor->pos: selpos;
827         p2 = (cursor->pos > selpos) ? cursor->pos: selpos;
828         selarray = cursor->Copy(p1, p2);
829         cursor->Clean(selpos);
830         cursor->Adjust();
831         SelClear();
832     }
833 }
834
835 void MathedCursor::SelDel()
836 {
837 //    fprintf(stderr, "Deleting sel ");
838     if (selection) {    
839         if (cursor->pos==selpos) return;
840         cursor->Clean(selpos);
841         cursor->Adjust();
842         SelClear();
843     }
844 }
845
846 void MathedCursor::SelPaste()
847 {
848 //    fprintf(stderr, "paste %p %d ", selarray, cursor->pos);
849     if (selection) SelDel();
850     if (selarray) {
851         cursor->Merge(selarray);
852         cursor->Adjust();
853     }
854 }
855
856 void MathedCursor::SelStart()
857 {
858    lyxerr.debug("Starting sel ",Error::MATHED);
859     if (!anchor) {
860         selpos = cursor->pos;   
861         selstk = new MathStackXIter(mathstk); 
862         anchor = selstk->Item(-1); 
863         anchor->SetData(cursor->p);
864         anchor->GoBegin();
865         anchor->goPosAbs(selpos);
866         selection = true;
867         
868     }
869 }
870
871 void MathedCursor::SelClear()
872 {   
873    lyxerr.debug("Clearing sel ", Error::MATHED);
874     selection = false;
875     delete selstk;
876     selstk = 0;
877     anchor = 0;
878 }
879
880
881
882 // Anchor position must be at the same level that stack.
883 void MathedCursor::SelBalance()
884 {
885     int d = mathstk.Level() - selstk->Level();
886
887     // If unbalanced, balance them
888     while (d != 0) {
889         if (d<0) {
890 //            fprintf(stderr, "b[%d %d %d %d]", mathstk.Level(), selstk->Level(), anchor->GetX(), cursor->GetX());
891             anchor = selstk->pop();
892             if (anchor->GetX() >= cursor->GetX()) 
893               anchor->Next();
894         } else {
895 //            fprintf(stderr, "a[%d %d]", mathstk.Level(), selstk->Level());
896             Pop();
897         }
898         d = mathstk.Level() - selstk->Level();
899     }
900
901     // Once balanced the levels, check that they are at the same paragraph
902     selpos = anchor->pos;
903
904
905
906 XPoint *MathedCursor::SelGetArea(int& np)
907 {   
908     if (!selection) {
909         np = 0;
910         return 0;
911     }
912     
913     static XPoint point[10];
914     
915     // single row selection
916     int i = 0, x, y, a, d, w, xo, yo, x1, y1, a1, d1; //, p1, p2;
917
918     // Balance anchor and cursor
919     SelBalance();
920  
921     cursor->p->GetXY(xo, yo);
922     w = cursor->p->Width();
923     cursor->GetPos(x1, y1);
924     cursor->getAD(a1, d1);
925     anchor->GetPos(x, y);
926     anchor->getAD(a, d);
927
928     point[i].x = x;
929     point[i++].y = y+d;
930     point[i].x = x;
931     point[i++].y = y-a;
932     
933     if (y!=y1) {
934         point[i].x = xo + w;
935         point[i++].y = y-a;
936         if (x1<xo+w) {
937             point[i].x = xo + w;
938             point[i++].y = y1-a;
939         }
940     }
941         
942     point[i].x = x1;
943     point[i++].y = y1-a;
944     point[i].x = x1;
945     point[i++].y = y1+d;
946     
947     if (y!=y1) {
948         point[i].x = xo;
949         point[i++].y = y1+d;
950         if (x>xo) {
951             point[i].x = xo;
952             point[i++].y = y+d;
953         }
954     }
955     point[i].x = point[0].x;
956     point[i++].y = point[0].y;
957     np = i;
958 //    fprintf(stderr, "AN[%d %d %d %d] ", x, y, x1, y1); 
959 //    fprintf(stderr, "MT[%d %d %d %d] ", a, d, a1, d1);
960 //    for (i=0; i<np; i++)
961 //      fprintf(stderr, "XY[%d %d] ", point[i].x, point[i].y);
962     
963     return &point[0];
964 }
965
966
967
968 void MathedCursor::setAccent(int ac)
969 {
970         if (ac > 0 && accent < 8) {
971                 nestaccent[accent++] = ac;
972         } else
973           accent = 0;  // consumed!
974 }
975
976  
977 int MathedCursor::getAccent() const
978 {
979         return (accent>0) ? nestaccent[accent-1]: 0;
980 }
981
982
983 void MathedCursor::doAccent(byte c, MathedTextCodes t)
984 {
985         MathedInset *ac = 0;
986         
987         for (int i=accent-1; i>=0; i--) {
988                 if (i==accent-1)
989                   ac = new MathAccentInset(c, t, nestaccent[i]);
990                 else 
991                   ac = new MathAccentInset(ac, nestaccent[i]);
992         }
993         if (ac) 
994           cursor->Insert(ac);
995         
996         accent = 0;  // consumed!
997 }
998
999
1000 void MathedCursor::doAccent(MathedInset *p)
1001 {
1002         MathedInset *ac = 0;
1003         
1004         for (int i=accent-1; i>=0; i--) {
1005                 if (i==accent-1)
1006                   ac = new MathAccentInset(p, nestaccent[i]);
1007                 else 
1008                   ac = new MathAccentInset(ac, nestaccent[i]);
1009         }
1010         if (ac) 
1011           cursor->Insert(ac);
1012         
1013         accent = 0;  // consumed!
1014 }
1015