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