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