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