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