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