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