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