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