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