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