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