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