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