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