]> git.lyx.org Git - lyx.git/blob - src/mathed/formula.C
e36ba7e85ed49de98efdf22a3f28617f61ebad75
[lyx.git] / src / mathed / formula.C
1 /*
2  *  File:        formula.h
3  *  Purpose:     Implementation of formula inset
4  *  Author:      Alejandro Aguilar Sierra <asierra@servidor.unam.mx> 
5  *  Created:     January 1996
6  *  Description: Allows the edition of math paragraphs inside Lyx. 
7  *
8  *  Copyright: (c) 1996-1998 Alejandro Aguilar Sierra
9  *
10  *  Version: 0.4, Lyx project.
11  *
12  *   You are free to use and modify this code under the terms of
13  *   the GNU General Public Licence version 2 or later.
14  */
15
16 #include <config.h>
17
18 #include <cctype>
19 #include <cstdlib>
20
21 #ifdef __GNUG__
22 #pragma implementation "formula.h"
23 #endif
24
25 #include "formula.h"
26 #include "commandtags.h"
27 #include "math_cursor.h"
28 #include "math_parser.h"
29 #include "lyx_main.h"
30 #include "bufferlist.h"
31 #include "lyx_cb.h"
32 #include "minibuffer.h"
33 #include "BufferView.h"
34 #include "lyxscreen.h"
35 #include "lyxdraw.h"
36 #include "lyxtext.h"
37 #include "gettext.h"
38 #include "LaTeXFeatures.h"
39 #include "debug.h"
40 #include "lyx_gui_misc.h"
41
42 extern void UpdateInset(Inset* inset, bool mark_dirty = true);
43 extern void LockedInsetStoreUndo(Undo::undo_kind);
44 extern MiniBuffer *minibuffer;
45 extern void ShowLockedInsetCursor(long, long, int, int);
46 extern void HideLockedInsetCursor(long, long, int, int);
47 extern void FitLockedInsetCursor(long, long, int, int);
48 extern int LockInset(UpdatableInset*);
49 extern int UnlockInset(UpdatableInset*);
50
51
52 extern GC canvasGC, mathGC, mathLineGC, latexGC, cursorGC, mathFrameGC;
53 extern char *mathed_label;
54
55 extern int mono_video;
56 extern int fast_selection;
57
58 extern BufferView *current_view;
59 extern BufferList bufferlist;
60 extern char const *latex_special_chars;
61
62 short greek_kb_flag = 0;
63
64 LyXFont *Math_Fonts = 0; // this is only used by Whichfont and mathed_init_fonts (Lgb)
65
66 static LyXFont::FONT_SIZE lfont_size = LyXFont::SIZE_NORMAL;
67
68 // local global 
69 static int sel_x, sel_y;
70 static bool sel_flag;
71 MathedCursor* InsetFormula::mathcursor = 0; 
72
73
74 int MathedInset::df_asc;
75 int MathedInset::df_des;
76 int MathedInset::df_width;
77
78 // wrong name on this one should be called "IsAscii"
79 inline bool IsAlpha(char c)
80 {
81    return ('A' <= c  && c<='Z' || 'a' <= c  && c<='z');
82 }
83
84 inline bool IsDigit(char c)
85 {
86    return ('0' <= c && c <='9');
87 }
88
89 inline bool IsMacro(short token, int id)
90 {
91    return (token!=LM_TK_FRAC && token!=LM_TK_SQRT &&
92           !((token==LM_TK_SYM || token==LM_TC_BSYM) && id<255));
93 }
94
95 void mathedValidate(LaTeXFeatures &features, MathParInset *par);
96
97 LyXFont WhichFont(short type, int size)
98 {
99     LyXFont f;
100     
101       if (!Math_Fonts)
102         mathed_init_fonts();
103    
104    switch (type) {
105     case LM_TC_SYMB:         
106       f = Math_Fonts[2];
107       break;
108     case LM_TC_BSYM:         
109       f = Math_Fonts[2];
110       break;
111     case LM_TC_VAR:
112     case LM_TC_IT:
113       f = Math_Fonts[0];
114       break;
115     case LM_TC_BF:
116       f = Math_Fonts[3];
117       break;
118     case LM_TC_SF:
119       f = Math_Fonts[7];
120       break;
121     case LM_TC_CAL:
122       f = Math_Fonts[4];
123       break;
124     case LM_TC_TT:
125       f = Math_Fonts[5];
126       break;
127     case LM_TC_SPECIAL: //f = Math_Fonts[0]; break;
128     case LM_TC_TEXTRM:
129     case LM_TC_RM:    
130       f = Math_Fonts[6];
131       break;
132     default:
133       f = Math_Fonts[1];
134       break;   
135    }
136     
137     f.setSize(lfont_size);
138     
139     switch (size) {
140      case LM_ST_DISPLAY:     
141         if (type==LM_TC_BSYM) {
142             f.incSize();
143             f.incSize();
144         }
145         break;
146      case LM_ST_TEXT:
147         break;
148      case LM_ST_SCRIPT:
149         f.decSize();
150         break;
151      case LM_ST_SCRIPTSCRIPT:
152         f.decSize();
153         f.decSize();
154         break;
155      default:
156              lyxerr << "Mathed Error: wrong font size: " << size << endl;
157         break;
158     }
159     
160     if (type!=LM_TC_TEXTRM) 
161       f.setColor(LyXFont::MATH);
162     return f;
163 }
164
165
166 void mathed_init_fonts() //removed 'static' because DEC cxx does not
167                          //like it (JMarc) 
168 {
169
170     Math_Fonts = new LyXFont[8]; //DEC cxx cannot initialize all fonts
171                                  //at once (JMarc) rc
172     for (int i=0 ; i<8 ; i++){ 
173         Math_Fonts[i] = LyXFont::ALL_SANE;
174     }
175     Math_Fonts[0].setShape(LyXFont::ITALIC_SHAPE);
176     
177     Math_Fonts[1].setFamily(LyXFont::SYMBOL_FAMILY);
178     
179     Math_Fonts[2].setFamily(LyXFont::SYMBOL_FAMILY);
180     Math_Fonts[2].setShape(LyXFont::ITALIC_SHAPE);
181
182     Math_Fonts[3].setSeries(LyXFont::BOLD_SERIES);
183       
184     Math_Fonts[4].setFamily(LyXFont::SANS_FAMILY);
185     Math_Fonts[4].setShape(LyXFont::ITALIC_SHAPE);
186       
187     Math_Fonts[5].setFamily(LyXFont::TYPEWRITER_FAMILY);
188
189     Math_Fonts[6].setFamily(LyXFont::ROMAN_FAMILY);
190
191     Math_Fonts[7].setFamily(LyXFont::SANS_FAMILY);
192     
193     LyXFont f = WhichFont(LM_TC_VAR, LM_ST_TEXT);
194     MathedInset::df_asc = f.maxAscent(); 
195     MathedInset::df_des = f.maxDescent();
196     MathedInset::df_width = f.width('I');    
197 }
198
199
200 void mathed_set_font(short type, int size)
201 {
202     if (!canvasGC) {
203             cursorGC = getGC(gc_thin_on_off_line);
204             canvasGC = getGC(gc_lighted);
205             latexGC =  getGC(gc_latex);
206             mathLineGC = getGC(gc_math);
207             mathFrameGC = getGC(gc_math_frame);
208     }
209     LyXFont f = WhichFont(type, size); 
210     if (type==LM_TC_TEX) {
211         f.setLatex(LyXFont::ON);
212         latexGC = f.getGC();
213     } else
214       mathGC = f.getGC();
215 }
216
217
218 int mathed_string_width(short type, int size, byte const* s, int ls)
219 {
220     LyXFont f = WhichFont(type, size);
221
222     byte sx[80];
223     if (MathIsBinary(type)) {
224         byte *ps = &sx[0];
225         for (int i=0; i<ls && i<75; i++) {
226             *(ps++) = ' ';
227             *(ps++) = s[i];
228             *(ps++) = ' ';
229         }
230         *(ps++) = '\0';
231         ls = 3*ls;
232         s = &sx[0];
233     }
234     return f.textWidth((const char*)s, ls);;
235 }
236
237 int mathed_char_width(short type, int size, byte c)
238 {
239     int t= (MathIsBinary(type)) ? mathed_string_width(type, size, &c, 1):
240            WhichFont(type, size).width(c);
241     return t;
242 }
243
244 int mathed_string_height(short type, int size, byte const * s, int ls, int& asc, int& des)
245 {
246    LyXFont font = WhichFont(type, size);
247    asc = des = 0;
248    for (int i=0; i<ls; i++) {
249       if (font.descent(s[i]) > des)
250         des = font.descent(s[i]);
251       if (font.ascent(s[i]) > asc)
252         asc = font.ascent(s[i]);
253    }
254    return asc+des;
255 }
256
257 int mathed_char_height(short type, int size, byte c, int& asc, int& des)
258 {
259    LyXFont font = WhichFont(type, size);
260    asc = des = 0;
261    des = font.descent(c);
262    asc = font.ascent(c);
263    return asc+des;
264 }
265
266
267 // In a near future maybe we use a better fonts renderer
268 void MathedInset::drawStr(short type, int size, int x, int y, byte* s, int ls)
269 {
270     mathed_set_font(type, size);
271     byte sx[80];
272     if (MathIsBinary(type)) {
273         byte *ps = &sx[0];
274         for (int i=0; i<ls && i < 75; i++) {
275             *(ps++) = ' ';
276             *(ps++) = s[i];
277             *(ps++) = ' ';
278         }
279         //    *ps = ' ';
280         ls = 3*ls;
281         s = &sx[0];
282     }
283     GC gc = (type==LM_TC_TEX) ? latexGC: mathGC;
284     XDrawString(fl_display, pm, gc, x, y, (char*)s, ls);
285     XFlush(fl_display);
286 }
287
288
289 InsetFormula::InsetFormula(bool display)
290 {
291   par = new MathParInset; // this leaks
292   //   mathcursor = 0;
293   disp_flag = display;
294   //label = 0;
295   if (disp_flag) {
296     par->SetType(LM_OT_PAR);
297     par->SetStyle(LM_ST_DISPLAY);
298   }
299 }
300
301 InsetFormula::InsetFormula(MathParInset *p)
302 {
303    par = (p->GetType()>=LM_OT_MPAR) ? 
304          new MathMatrixInset((MathMatrixInset*)p): 
305          new MathParInset(p);
306 //   mathcursor = 0;
307    
308    disp_flag = (par->GetType()>0);
309    //label = 0;
310 }
311
312 InsetFormula::~InsetFormula()
313 {
314    delete par;
315    //if (label) delete label;
316 }
317
318 Inset* InsetFormula::Clone()
319 {
320     InsetFormula* f = new InsetFormula(par);
321     f->label = label;
322     return (Inset*)f;
323 }
324
325 void InsetFormula::Write(FILE *file)
326 {
327    fprintf(file, "Formula ");
328    Latex(file, 0);
329 }
330
331 int InsetFormula::Latex(FILE *file, signed char fragile)
332 {
333     int ret = 0;      
334 //#warning Alejandro, the number of lines is not returned in this case
335 // This problem will disapear at 0.13.
336     string output;
337     InsetFormula::Latex(output, fragile);
338     fprintf(file, "%s", output.c_str());
339     return ret;
340 }
341
342 int InsetFormula::Latex(string &file, signed char fragile)
343 {
344     int ret = 0;        
345 //#warning Alejandro, the number of lines is not returned in this case
346 // This problem will disapear at 0.13.
347     if (fragile < 0)
348         par->Write(file);
349     else
350         mathed_write(par, file, &ret, fragile, label.c_str());
351     return ret;
352 }
353
354
355 int InsetFormula::Linuxdoc(string &/*file*/)
356 {
357     return 0;
358 }
359
360
361 int InsetFormula::DocBook(string &/*file*/)
362 {
363     return 0;
364 }
365
366
367 // Check if uses AMS macros 
368 void InsetFormula::Validate(LaTeXFeatures &features) const
369 {
370     // Validation only necesary if not using an AMS Style
371     if (!features.amsstyle)
372       mathedValidate(features, par);
373 }
374
375
376 void InsetFormula::Read(LyXLex &lex)
377 {
378     FILE *file = lex.getFile();
379     
380     mathed_parser_file(file, lex.GetLineNo());   
381    
382    // Silly hack to read labels. 
383    mathed_label = 0;
384    mathed_parse(0, 0, &par);
385    par->Metrics();
386    disp_flag = (par->GetType()>0);
387    
388     // Update line number
389     lex.setLineNo(mathed_parser_lineno());
390     
391    if (mathed_label) {
392       label = mathed_label;
393       mathed_label = 0;
394    }
395    
396 #ifdef DEBUG
397    Write(stdout);
398    fflush(stdout);
399 #endif
400 }
401
402 int InsetFormula::Ascent(LyXFont const &) const
403 {
404    return par->Ascent() + ((disp_flag) ? 8: 1);
405 }
406
407 int InsetFormula::Descent(LyXFont const &) const
408 {
409    return par->Descent() + ((disp_flag) ? 8: 1);
410 }
411
412 int InsetFormula::Width(LyXFont const &f) const
413 {
414     lfont_size = f.size();
415     par->Metrics();
416     return par->Width(); //+2;
417 }
418
419 void InsetFormula::Draw(LyXFont f, LyXScreen &scr, int baseline, float &x)
420 {
421         // This is Alejandros domain so I'll use this
422         unsigned long pm = scr.getForeground();
423         
424    lfont_size = f.size();
425    mathed_set_font(LM_TC_TEXTRM, LM_ST_TEXT); // otherwise a segfault could occur
426                                                                 // in some XDrawRectangles (i.e. matrix) (Matthias)   
427    if (mathcursor && mathcursor->GetPar()==par) { 
428        if (mathcursor->Selection()) {
429            int n;
430            XPoint * p = mathcursor->SelGetArea(n);
431            XFillPolygon(fl_display, pm, getGC(gc_selection), //LyXGetSelectionGC(),
432                         p, n, Nonconvex, CoordModeOrigin);
433        }
434        mathcursor->Draw(pm, (int)x, baseline);
435    } else {
436 //       par->Metrics();
437        par->setDrawable(pm);
438        par->Draw((int)x, baseline);
439    }
440    x += (float)Width(f);
441  
442    if (par->GetType()==LM_OT_PARN || par->GetType()==LM_OT_MPARN) {
443        char s[80];
444        LyXFont  font = WhichFont(LM_TC_BF, par->size);
445        font.setLatex(LyXFont::OFF);
446       
447        if (par->GetType()==LM_OT_PARN) {
448            if (!label.empty())
449              sprintf(s, "(%s)", label.c_str());
450            else
451              sprintf(s, "(#)");
452            font.drawString(s, pm, baseline, int(x+20));
453        } else 
454        if (par->GetType()==LM_OT_MPARN) {
455            MathMatrixInset *mt = (MathMatrixInset*)par;
456            //int i=0;
457            int y;
458            MathedRowSt const* crow = mt->getRowSt();
459            while (crow) {
460                y = baseline + crow->getBaseline();
461                if (crow->isNumbered()) {
462                    if (crow->getLabel())
463                      sprintf(s, "(%s)", crow->getLabel());
464                    else
465                      sprintf(s, "(#)");
466                    font.drawString(s, pm, y, int(x+20));
467                }
468                crow = crow->getNext();
469            }
470        }
471    }
472    cursor_visible = false;
473 }
474
475
476 void InsetFormula::Edit(int x, int y)
477 {
478    mathcursor = new MathedCursor(par);
479    LockInset(this);
480    par->Metrics();
481    UpdateInset(this, false);
482    x += par->xo; 
483    y += par->yo; 
484    mathcursor->SetPos(x, y);
485     sel_x = sel_y = 0;
486     sel_flag = false;
487 }
488                                        
489 void InsetFormula::InsetUnlock()
490 {
491    if (mathcursor) {
492        if (mathcursor->InMacroMode()) {
493            mathcursor->MacroModeClose();
494            UpdateLocal();
495        }                                         
496      delete mathcursor;
497    }
498    mathcursor = 0;
499    UpdateInset(this, false);
500 }
501
502 // Now a symbol can be inserted only if the inset is locked
503 void InsetFormula::InsertSymbol(char const* s)
504
505    if (!s || !mathcursor) return;   
506    mathcursor->Interpret(s);
507    UpdateLocal();
508 }
509    
510 void InsetFormula::GetCursorPos(int& x, int& y)
511 {
512     mathcursor->GetPos(x, y);
513     x -= par->xo; 
514     y -= par->yo;
515 }
516
517 void InsetFormula::ToggleInsetCursor()
518 {
519   if (!mathcursor)
520     return;
521
522   int x, y, asc, desc;
523   mathcursor->GetPos(x, y);
524 //  x -= par->xo; 
525   y -= par->yo; 
526     LyXFont font = WhichFont(LM_TC_TEXTRM, LM_ST_TEXT);
527   asc = font.maxAscent();
528   desc = font.maxDescent();
529   
530   if (cursor_visible)
531     HideLockedInsetCursor(x, y, asc, desc);
532   else
533     ShowLockedInsetCursor(x, y, asc, desc);
534   cursor_visible = !cursor_visible;
535 }
536
537 void InsetFormula::ShowInsetCursor(){
538   if (!cursor_visible){
539     int x, y, asc, desc;
540     if (mathcursor){
541       mathcursor->GetPos(x, y);
542       //  x -= par->xo; 
543       y -= par->yo;
544         LyXFont font = WhichFont(LM_TC_TEXTRM, LM_ST_TEXT);
545         asc = font.maxAscent();
546         desc = font.maxDescent();
547       FitLockedInsetCursor(x, y, asc, desc);
548     }
549     ToggleInsetCursor();
550   }
551 }
552
553 void InsetFormula::HideInsetCursor(){
554   if (cursor_visible)
555     ToggleInsetCursor();
556 }
557
558 void InsetFormula::ToggleInsetSelection()
559 {
560     if (!mathcursor)
561       return;
562     
563 //    int x, y, w, h;
564     //int n;
565     //XPoint * p = 
566     //mathcursor->SelGetArea(n);
567 //    XFillPolygon(fl_display, pm, LyXGetSelectionGC(), p, n, Nonconvex, CoordModeOrigin);
568 //    x -= par->xo; 
569 //    y -= par->yo;
570
571     UpdateInset(this, false);
572       
573 }
574
575 void InsetFormula::SetDisplay(bool dspf)
576 {
577    if (dspf!=disp_flag) {
578       if (dspf) {
579          par->SetType(LM_OT_PAR);
580          par->SetStyle(LM_ST_DISPLAY);
581       } else {
582          if (par->GetType()>=LM_OT_MPAR) { 
583             MathParInset *p = new MathParInset(par);
584             delete par;
585             par = p;
586             if (mathcursor) 
587                mathcursor->SetPar(par); 
588          }
589          par->SetType(LM_OT_MIN);
590          par->SetStyle(LM_ST_TEXT);
591          if (!label.empty() && par->GetType()!=LM_OT_MPARN) {
592                  label.clear();
593          }
594       }
595       disp_flag = dspf;
596    }   
597 }
598    
599
600
601 int InsetFormula::GetNumberOfLabels() const
602 {
603    // This is dirty, I know. I'll clean it at 0.13
604    if (par->GetType()==LM_OT_MPARN) {
605        MathMatrixInset *mt = (MathMatrixInset*)par;
606        int nl=0;
607        MathedRowSt const* crow = mt->getRowSt();
608        while (crow) {
609            if (crow->getLabel()) nl++;
610            crow = crow->getNext();
611        }
612        return nl;
613    } else
614    if (!label.empty())
615        return 1;
616    else
617        return 0;
618 }
619
620
621 string InsetFormula::getLabel(int il) const
622 {
623 //#warning This is dirty, I know. Ill clean it at 0.11
624     // Correction, the only way to clean this is with a new kernel: 0.13.
625         if (par->GetType()==LM_OT_MPARN) {
626        string label;
627        MathMatrixInset *mt = (MathMatrixInset*)par;
628        int nl=0;
629        MathedRowSt const* crow = mt->getRowSt();
630        while (crow) {
631            if (crow->getLabel()) {
632                if (nl==il) {
633                    label = crow->getLabel();
634                    break;
635                }
636                nl++;
637            }
638            crow = crow->getNext();
639        }
640        return label;
641    }
642    return label;
643 }
644
645 void InsetFormula::UpdateLocal()
646 {
647    par->Metrics();  // To inform lyx kernel the exact size 
648                   // (there were problems with arrays).
649    UpdateInset(this);
650 }
651    
652
653
654 void InsetFormula::InsetButtonRelease(int x, int y, int /*button*/)
655 {
656     HideInsetCursor();
657     x += par->xo;
658     y += par->yo;
659     mathcursor->SetPos(x, y);
660     ShowInsetCursor();
661     if (sel_flag) {
662         sel_flag = false; 
663         sel_x = sel_y = 0;
664         UpdateInset(this,false); 
665     }
666 }
667
668 void InsetFormula::InsetButtonPress(int x, int y, int /*button*/)
669 {
670     sel_flag = false;
671     sel_x = x;  sel_y = y; 
672     if (mathcursor->Selection()) {
673         mathcursor->SelClear();
674         UpdateInset(this, false); 
675     }
676 }
677
678 void InsetFormula::InsetMotionNotify(int x, int y, int /*button*/)
679 {
680     if (sel_x && sel_y && abs(x-sel_x)>4 && !sel_flag) {
681         sel_flag = true;
682         HideInsetCursor();
683         mathcursor->SetPos(sel_x + par->xo, sel_y + par->yo);
684         mathcursor->SelStart();
685         ShowInsetCursor(); 
686         mathcursor->GetPos(sel_x, sel_y);
687     } else
688       if (sel_flag) {
689           HideInsetCursor();
690           x += par->xo;
691           y += par->yo;
692           mathcursor->SetPos(x, y);
693           ShowInsetCursor();
694           mathcursor->GetPos(x, y);
695           if (sel_x!=x || sel_y!=y)
696             UpdateInset(this, false); 
697           sel_x = x;  sel_y = y;
698       }
699 }
700
701 void InsetFormula::InsetKeyPress(XKeyEvent *)
702 {
703         lyxerr[Debug::MATHED] << "Used InsetFormula::InsetKeyPress." << endl;
704 }
705
706 // Special Mathed functions
707 bool InsetFormula::SetNumber(bool numbf)
708 {
709    if (disp_flag) {
710       short type = par->GetType();
711       bool oldf = (type==LM_OT_PARN || type==LM_OT_MPARN);
712       if (numbf && !oldf) type++;
713       if (!numbf && oldf) type--;
714       par->SetType(type);
715       return oldf;
716    } else
717      return false;
718 }
719
720 bool InsetFormula::LocalDispatch(int action, char const *arg)
721 {
722 //   extern char *dispatch_result;
723     MathedTextCodes varcode = LM_TC_MIN;       
724    bool was_macro = mathcursor->InMacroMode();
725    bool sel=false;
726    bool space_on = false;
727    bool was_selection = mathcursor->Selection();
728    bool result = true;
729    static MathSpaceInset* sp=0;
730
731    HideInsetCursor();
732    if (mathcursor->Selection() && (fast_selection || mono_video)) ToggleInsetSelection();
733
734     if (mathcursor->getLastCode()==LM_TC_TEX) { 
735         varcode = LM_TC_TEX;
736     }
737    switch (action) {
738        
739     // --- Cursor Movements ---------------------------------------------
740     case LFUN_RIGHTSEL: sel = true;
741     case LFUN_RIGHT:
742       {
743          result = mathcursor->Right(sel);
744          break;
745       }
746     case LFUN_LEFTSEL: sel = true;     
747     case LFUN_LEFT:
748       {
749          result = mathcursor->Left(sel);
750          break;
751       }
752     case LFUN_UPSEL: sel = true;  
753     case LFUN_UP:
754       result = mathcursor->Up(sel);
755       break;
756        
757     case LFUN_DOWNSEL: sel = true;  
758     case LFUN_DOWN:
759       result = mathcursor->Down(sel);
760       break;
761     case LFUN_HOME:
762       mathcursor->Home();
763       break;
764     case LFUN_END:
765       mathcursor->End();
766       break;
767     case LFUN_DELETE_LINE_FORWARD:
768             //LockedInsetStoreUndo(Undo::INSERT);
769             LockedInsetStoreUndo(Undo::DELETE);
770       mathcursor->DelLine();
771       UpdateLocal();
772       break;
773     case LFUN_BREAKLINE:
774       LockedInsetStoreUndo(Undo::INSERT);
775       mathcursor->Insert(' ', LM_TC_CR);
776       par = mathcursor->GetPar();
777       UpdateLocal();     
778       break;
779     case LFUN_TAB:
780       LockedInsetStoreUndo(Undo::INSERT);
781       mathcursor->Insert(0, LM_TC_TAB);
782       //UpdateInset(this);
783       break;     
784     case LFUN_TABINSERT:
785       LockedInsetStoreUndo(Undo::INSERT);
786       mathcursor->Insert('T', LM_TC_TAB);
787       UpdateLocal();
788       break;     
789     case LFUN_BACKSPACE:
790        if (!mathcursor->Left()) 
791          break;
792        
793        if (!mathcursor-> InMacroMode() && mathcursor->pullArg()) {       
794            UpdateInset(this);
795            break;
796        }
797       
798     case LFUN_DELETE:
799             //LockedInsetStoreUndo(Undo::INSERT);
800             LockedInsetStoreUndo(Undo::DELETE);
801       mathcursor->Delete();       
802       UpdateInset(this);
803       break;    
804 //    case LFUN_GETXY:
805 //      sprintf(dispatch_buffer, "%d %d",);
806 //      dispatch_result = dispatch_buffer;
807 //      break;
808     case LFUN_SETXY:
809       {
810          int x, y, x1, y1;
811          sscanf(arg, "%d %d", &x, &y);
812          par->GetXY(x1, y1);
813          mathcursor->SetPos(x1+x, y1+y);
814       }
815       break;
816
817       /* cursor selection ---------------------------- */
818
819     case LFUN_PASTE:
820             if (was_macro)
821                 mathcursor->MacroModeClose();
822             LockedInsetStoreUndo(Undo::INSERT);
823             mathcursor->SelPaste(); UpdateLocal(); break;
824     case LFUN_CUT:
825             LockedInsetStoreUndo(Undo::DELETE);
826             mathcursor->SelCut(); UpdateLocal(); break;
827     case LFUN_COPY: mathcursor->SelCopy(); break;      
828     case LFUN_HOMESEL:
829     case LFUN_ENDSEL:
830     case LFUN_WORDRIGHTSEL:
831     case LFUN_WORDLEFTSEL:
832       break;
833       
834     // --- accented characters ------------------------------
835
836     case LFUN_UMLAUT: mathcursor->setAccent(LM_ddot); break;
837     case LFUN_CIRCUMFLEX: mathcursor->setAccent(LM_hat); break;
838     case LFUN_GRAVE: mathcursor->setAccent(LM_grave); break;
839     case LFUN_ACUTE: mathcursor->setAccent(LM_acute); break;
840     case LFUN_TILDE: mathcursor->setAccent(LM_tilde); break;
841     case LFUN_MACRON: mathcursor->setAccent(LM_bar); break;
842     case LFUN_DOT: mathcursor->setAccent(LM_dot); break;
843     case LFUN_CARON: mathcursor->setAccent(LM_check); break;
844     case LFUN_BREVE: mathcursor->setAccent(LM_breve); break;
845     case LFUN_VECTOR: mathcursor->setAccent(LM_vec); break; 
846       
847     // Greek mode     
848     case LFUN_GREEK:
849     {
850        if (!greek_kb_flag) {
851           greek_kb_flag = 1;
852           minibuffer->Set(_("Math greek mode on"));
853        } else
854          greek_kb_flag = 0;
855        break;
856     }  
857       
858     // Greek keyboard      
859     case LFUN_GREEK_TOGGLE:
860     {
861        greek_kb_flag = (greek_kb_flag) ? 0: 2;
862        if (greek_kb_flag)
863          minibuffer->Set(_("Math greek keyboard on"));
864        else
865          minibuffer->Set(_("Math greek keyboard off"));
866        break;
867     }  
868    
869       //  Math fonts 
870     case LFUN_BOLD:     mathcursor->setLastCode(LM_TC_BF); break;
871     case LFUN_SANS:  mathcursor->setLastCode( LM_TC_SF); break;
872     case LFUN_EMPH:  mathcursor->setLastCode(LM_TC_CAL); break;
873     case LFUN_ROMAN: mathcursor->setLastCode(LM_TC_RM); break;
874     case LFUN_CODE: mathcursor->setLastCode(LM_TC_TT); break;   
875     case LFUN_DEFAULT:  mathcursor->setLastCode(LM_TC_VAR ) ; break;
876     case LFUN_TEX: 
877     {
878 //       varcode = LM_TC_TEX;
879         mathcursor->setLastCode(LM_TC_TEX);
880        minibuffer->Set(_("TeX mode")); 
881        break;
882     }
883
884     case LFUN_MATH_NUMBER:
885     {
886       LockedInsetStoreUndo(Undo::INSERT);
887        if (disp_flag) {
888           short type = par->GetType();
889           bool oldf = (type==LM_OT_PARN || type==LM_OT_MPARN);
890           if (oldf) {
891              type--;
892              if (!label.empty()) {
893                      label.clear();
894              }
895              minibuffer->Set(_("No number"));  
896           } else {
897              type++;
898              minibuffer->Set(_("Number"));
899           }
900           par->SetType(type);
901           UpdateLocal();
902        }
903        break;
904     }
905     
906     case LFUN_MATH_NONUMBER:
907     { 
908         if (par->GetType()==LM_OT_MPARN) {
909 //         MathMatrixInset *mt = (MathMatrixInset*)par;
910            //BUG 
911 //         mt->SetNumbered(!mt->IsNumbered());
912             
913             mathcursor->setNumbered();
914            UpdateLocal();
915         }
916         break;
917     }
918        
919     case LFUN_MATH_LIMITS:
920     {
921       LockedInsetStoreUndo(Undo::INSERT);
922        if (mathcursor->Limits())
923          UpdateLocal();
924     }
925  
926     case LFUN_MATH_SIZE:
927        if (arg) {
928            latexkeys *l = in_word_set (arg, strlen(arg));
929            int sz = (l) ? l->id: -1;
930            mathcursor->SetSize(sz);
931            UpdateLocal();
932            break;
933        }
934        
935     case LFUN_INSERT_MATH:
936     {
937         LockedInsetStoreUndo(Undo::INSERT);
938         InsertSymbol(arg);
939         break;
940     }
941     
942     case LFUN_INSERT_MATRIX:
943     { 
944       LockedInsetStoreUndo(Undo::INSERT);
945        int k, m, n;
946        char s[80], arg2[80];
947        // This is just so that too long args won't ooze out of s.
948        strncpy(arg2,arg,80); arg2[79]=(char)0;
949        k = sscanf(arg2, "%d %d %s", &m, &n, s);
950        s[79] = (char)0;
951         
952        if (k<1) {
953            m = n = 1;
954        } else if (k==1) {
955            n = 1;
956        }
957         
958        MathMatrixInset *p = new MathMatrixInset(m, n);      
959        if (mathcursor && p) {
960           if (k>2 && (int)strlen(s)>m)
961             p->SetAlign(s[0], &s[1]);
962           mathcursor->Insert(p, LM_TC_ACTIVE_INSET);
963           UpdateLocal();     
964        }
965        break;
966     }
967       
968     case LFUN_MATH_DELIM:
969     {  
970       LockedInsetStoreUndo(Undo::INSERT);
971        char lf[40], rg[40], arg2[40];
972        int ilf = '(', irg = '.';
973        latexkeys *l;
974        string vdelim("(){}[]./|");
975         
976        if (!arg) break;
977        strncpy(arg2,arg,40); arg2[39]=(char)0;
978        int n = sscanf(arg2, "%s %s", lf, rg);
979        lf[39] = (char)0; rg[39] = (char)0;
980
981        if (n>0) {
982            if (IsDigit(lf[0])) 
983              ilf = atoi(lf);
984            else 
985              if (lf[1]) {
986                  l = in_word_set(lf, strlen(lf));
987                  // Long words will cause l==0; so check.
988                  if(l) ilf = l->id;
989              } else
990              if (vdelim.find(lf[0]) != string::npos)
991                ilf = lf[0];
992            
993            if (n>1) {
994                if (IsDigit(rg[0]))
995                  irg = atoi(rg);
996                else 
997                  if (rg[1]) {
998                      l = in_word_set(rg, strlen(rg));
999                      if(l) irg = l->id;
1000                  } else
1001                  if (vdelim.find(rg[0]) != string::npos)
1002                    irg = rg[0];
1003            }
1004        }
1005        
1006        MathDelimInset* p = new MathDelimInset(ilf, irg);
1007        mathcursor->Insert(p, LM_TC_ACTIVE_INSET);
1008        UpdateLocal();                             
1009        break;
1010     }
1011
1012     case LFUN_PROTECTEDSPACE:
1013     {
1014       LockedInsetStoreUndo(Undo::INSERT);
1015        sp = new MathSpaceInset(1); 
1016        mathcursor->Insert(sp);
1017        space_on = true;
1018        UpdateLocal();
1019        break;
1020     }
1021       
1022     case LFUN_INSERT_LABEL:
1023     {
1024       LockedInsetStoreUndo(Undo::INSERT);
1025        if (par->GetType()<LM_OT_PAR) break;
1026        string lb = arg;
1027        if (lb.empty())
1028               lb = string(askForText(_("Enter new label to insert:")));
1029        if (!lb.empty() && lb[0]> ' ') {
1030           SetNumber(true);
1031           if (par->GetType()==LM_OT_MPARN) {
1032               mathcursor->setLabel(lb.c_str());
1033 //            MathMatrixInset *mt = (MathMatrixInset*)par;
1034 //            mt->SetLabel(lb);
1035           } else {
1036                   //if (label.notEmpty()) delete label;
1037               label = lb;
1038           }
1039           UpdateLocal();
1040        } else
1041                label.clear();
1042                //label = 0;
1043        break;
1044     }
1045     
1046     case LFUN_MATH_DISPLAY:
1047             //LockedInsetStoreUndo(Undo::INSERT);
1048             LockedInsetStoreUndo(Undo::EDIT);
1049       SetDisplay(!disp_flag);
1050       UpdateLocal();
1051       break;
1052       
1053     // Invalid actions under math mode
1054     case LFUN_MATH_MODE:  
1055     {
1056         if (mathcursor->getLastCode()!=LM_TC_TEXTRM) {
1057             minibuffer->Set(_("math text mode"));
1058             varcode = LM_TC_TEXTRM;
1059         } else {
1060             varcode = LM_TC_VAR;
1061         }
1062         mathcursor->setLastCode(varcode);
1063         break; 
1064     }
1065     case LFUN_UNDO:
1066       minibuffer->Set(_("Invalid action in math mode!"));
1067       break;
1068
1069     //------- dummy actions
1070     case LFUN_EXEC_COMMAND:
1071        minibuffer->ExecCommand(); 
1072        break;
1073        
1074     default:
1075       if ((action==-1  || action==LFUN_SELFINSERT) && arg)  {
1076          unsigned char c = arg[0];
1077          LockedInsetStoreUndo(Undo::INSERT);
1078          
1079          if (c==' ' && mathcursor->getAccent()==LM_hat) {
1080              c = '^';
1081              mathcursor->setAccent(0);
1082          }
1083          if (c==0) {      // Dead key, do nothing 
1084              //lyxerr << "deadkey" << endl;
1085              break;
1086          } 
1087          if (isalpha(c)) {
1088              if (mathcursor->getLastCode()==LM_TC_TEX) { 
1089                mathcursor->MacroModeOpen();
1090                mathcursor->clearLastCode();
1091                varcode = LM_TC_MIN;
1092             } else          
1093             if (!varcode) {             
1094                 short f = (mathcursor->getLastCode()) ? 
1095                           mathcursor->getLastCode():
1096                           (MathedTextCodes)mathcursor->GetFCode();
1097                varcode =  MathIsAlphaFont(f) ?  (MathedTextCodes)f:LM_TC_VAR;
1098             }
1099
1100 //           lyxerr << "Varcode << vardoce;
1101             mathcursor->Insert(c, (greek_kb_flag) ? LM_TC_SYMB: varcode);
1102             varcode = LM_TC_MIN;
1103             if (greek_kb_flag<2) greek_kb_flag = 0;
1104          } else 
1105            if (strchr("!,:;{}", c) && (varcode==LM_TC_TEX||was_macro)) {
1106                mathcursor->Insert(c, LM_TC_TEX);
1107                if (c=='{') {
1108                    mathcursor->Insert('}', LM_TC_TEX);
1109                    mathcursor->Left();
1110                }
1111                mathcursor->clearLastCode();
1112 //             varcode = LM_TC_MIN;
1113            } else
1114            if (c=='_' && varcode==LM_TC_TEX) {
1115                mathcursor->Insert(c, LM_TC_SPECIAL);
1116                mathcursor->clearLastCode();
1117 //             varcode = LM_TC_MIN;
1118            } else
1119             if (('0'<=c && c<='9') && (varcode==LM_TC_TEX||was_macro)) {
1120                 mathcursor->MacroModeOpen();
1121                 mathcursor->clearLastCode();
1122                 mathcursor->Insert(c, LM_TC_MIN);
1123             }
1124          else
1125            if (('0'<=c && c<='9') || strchr(";:!|[]().,?", c)) 
1126               mathcursor->Insert(c, LM_TC_CONST);
1127          else
1128            if (strchr("+/-*<>=", c))
1129               mathcursor->Insert(c, LM_TC_BOP);
1130          else
1131            if (strchr(latex_special_chars, c) && c!='_')
1132               mathcursor->Insert(c, LM_TC_SPECIAL);
1133          else
1134            if (c=='_' || c=='^') {
1135                char s[2];
1136                s[0] = c;
1137                s[1] = 0;
1138               mathcursor->Interpret (s);
1139            } else
1140            if (c==' ') {            
1141                if (!varcode) {  
1142                    short f = (mathcursor->getLastCode()) ? 
1143                               mathcursor->getLastCode():
1144                               (MathedTextCodes)mathcursor->GetFCode();
1145                    varcode = MathIsAlphaFont(f) ? (MathedTextCodes)f:LM_TC_VAR;
1146                }
1147               if (varcode==LM_TC_TEXTRM) {
1148                   mathcursor->Insert(c, LM_TC_TEXTRM);
1149               } else
1150               if (was_macro)
1151                 mathcursor->MacroModeClose();
1152               else 
1153               if (sp) {
1154                  int isp = (sp->GetSpace()<5) ? sp->GetSpace()+1: 0;
1155                  sp->SetSpace(isp);
1156                  space_on = true;
1157               } else {
1158                   if (!mathcursor->Pop() && mathcursor->IsEnd()) 
1159                     result = false;
1160               }
1161            } else
1162            if (c=='\'') {
1163               mathcursor->Insert (c, LM_TC_VAR);
1164            } else
1165            if (c=='\\') {
1166               if (was_macro)
1167                 mathcursor->MacroModeClose();
1168               minibuffer->Set(_("TeX mode")); 
1169                mathcursor->setLastCode(LM_TC_TEX);
1170            } 
1171          UpdateLocal();
1172       } else {
1173         // lyxerr << "Closed by action " << action << endl;
1174         result =  false;
1175       }
1176    }
1177    if (was_macro!=mathcursor->InMacroMode()&&action>=0&&action!=LFUN_BACKSPACE)
1178      UpdateLocal();
1179    if (sp && !space_on) sp = 0;
1180    if (mathcursor->Selection() || (was_selection && !(fast_selection || mono_video)))
1181        ToggleInsetSelection();
1182     
1183    if (result)
1184       ShowInsetCursor();
1185    else
1186       UnlockInset(this);
1187     
1188    return result;
1189 }
1190
1191
1192 void
1193 MathFuncInset::Draw(int x, int y)
1194
1195         if (name && name[0]>' ') {
1196                 LyXFont  font = WhichFont(LM_TC_TEXTRM, size);
1197                 font.setLatex(LyXFont::ON);
1198                 x += (font.textWidth("I", 1)+3)/4;
1199                 if (mono_video) {
1200                         int a=font.maxAscent(), d=font.maxDescent();
1201                         XFillRectangle (fl_display, pm, getGC(gc_copy),
1202                                         x, y-a,
1203                                         font.textWidth(name, strlen(name)), a+d);
1204                         XFlush(fl_display);
1205                 }
1206                 font.drawString(name, pm, y, x);
1207         }
1208 }
1209
1210
1211 void MathFuncInset::Metrics() 
1212 {
1213         ln = (name) ? strlen(name): 0;
1214         LyXFont  font = WhichFont(LM_TC_TEXTRM, size);
1215         font.setLatex(LyXFont::ON);
1216         width = font.textWidth(name, ln) + font.textWidth("I", 1)/2;
1217         mathed_string_height(LM_TC_TEXTRM, size, (byte const *) name,
1218                              strlen(name), ascent, descent);
1219 }
1220
1221
1222 void mathedValidate(LaTeXFeatures &features, MathParInset *par)
1223 {
1224     MathedIter it(par->GetData());
1225     
1226     while (it.OK() && !(features.binom && features.boldsymbol)) {
1227         if (it.IsInset()) {
1228             if(it.IsActive()) {
1229                 MathParInset *p = it.GetActiveInset();
1230                 if (!features.binom && p->GetType()==LM_OT_MACRO && 
1231                     strcmp(p->GetName(), "binom")==0) {
1232                     features.binom = true;
1233                 } else {
1234                     for (int i=0; i<=p->getMaxArgumentIdx(); i++) {
1235                         p->setArgumentIdx(i);
1236                         mathedValidate(features, p);
1237                     }
1238                 }
1239             } else {
1240                 MathedInset* p = it.GetInset();
1241                 if (!features.boldsymbol && p->GetName() &&
1242                     strcmp(p->GetName(), "boldsymbol")==0) {
1243                     features.boldsymbol = true;
1244                 }
1245             }       
1246         }
1247         it.Next();
1248     }
1249 }