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