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