]> git.lyx.org Git - lyx.git/blob - src/mathed/formulabase.C
coordinate cache IU
[lyx.git] / src / mathed / formulabase.C
1 /**
2  * \file formulabase.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "cursor.h"
15 #include "formulabase.h"
16 #include "formula.h"
17 #include "formulamacro.h"
18 #include "math_support.h"
19 #include "math_arrayinset.h"
20 #include "math_deliminset.h"
21 #include "math_cursor.h"
22 #include "math_factory.h"
23 #include "math_hullinset.h"
24 #include "math_parser.h"
25 #include "math_spaceinset.h"
26 #include "ref_inset.h"
27
28 #include "BufferView.h"
29 #include "bufferview_funcs.h"
30 #include "dispatchresult.h"
31 #include "debug.h"
32 #include "funcrequest.h"
33 #include "gettext.h"
34 #include "LColor.h"
35 #include "lyxtext.h"
36 #include "undo.h"
37
38 #include "frontends/LyXView.h"
39 #include "frontends/Dialogs.h"
40
41 #include "support/std_sstream.h"
42 #include "support/lstrings.h"
43 #include "support/lyxlib.h"
44
45 using lyx::support::atoi;
46 using lyx::support::split;
47 using lyx::support::token;
48
49 using std::string;
50 using std::abs;
51 using std::endl;
52 using std::max;
53
54 using std::istringstream;
55
56
57 MathCursor * mathcursor = 0;
58
59 namespace {
60
61 // local global
62 int first_x;
63 int first_y;
64
65 bool openNewInset(BufferView * bv, UpdatableInset * inset)
66 {
67         if (!bv->insertInset(inset)) {
68                 delete inset;
69                 return false;
70         }
71         inset->edit(bv, true);
72         return true;
73 }
74
75
76 } // namespace anon
77
78
79
80 InsetFormulaBase::InsetFormulaBase()
81 {
82         // This is needed as long the math parser is not re-entrant
83         initMath();
84         //lyxerr << "sizeof MathInset: " << sizeof(MathInset) << endl;
85         //lyxerr << "sizeof MetricsInfo: " << sizeof(MetricsInfo) << endl;
86         //lyxerr << "sizeof MathCharInset: " << sizeof(MathCharInset) << endl;
87         //lyxerr << "sizeof LyXFont: " << sizeof(LyXFont) << endl;
88 }
89
90
91 // simply scrap this function if you want
92 void InsetFormulaBase::mutateToText()
93 {
94 #if 0
95         // translate to latex
96         ostringstream os;
97         latex(NULL, os, false, false);
98         string str = os.str();
99
100         // insert this text
101         LyXText * lt = view_->getLyXText();
102         string::const_iterator cit = str.begin();
103         string::const_iterator end = str.end();
104         for (; cit != end; ++cit)
105                 view_->owner()->getIntl()->getTransManager().TranslateAndInsert(*cit, lt);
106
107         // remove ourselves
108         //view_->owner()->dispatch(LFUN_ESCAPE);
109 #endif
110 }
111
112
113 void InsetFormulaBase::handleFont
114         (BufferView * bv, string const & arg, string const & font)
115 {
116         // this whole function is a hack and won't work for incremental font
117         // changes...
118         recordUndo(bv, Undo::ATOMIC);
119
120         if (mathcursor->inset()->name() == font)
121                 mathcursor->handleFont(font);
122         else {
123                 mathcursor->handleNest(createMathInset(font));
124                 mathcursor->insert(arg);
125         }
126 }
127
128
129 void InsetFormulaBase::handleFont2(BufferView * bv, string const & arg)
130 {
131         recordUndo(bv, Undo::ATOMIC);
132         LyXFont font;
133         bool b;
134         bv_funcs::string2font(arg, font, b);
135         if (font.color() != LColor::inherit) {
136                 MathAtom at = createMathInset("color");
137                 asArray(lcolor.getGUIName(font.color()), at.nucleus()->cell(0));
138                 mathcursor->handleNest(at, 1);
139         }
140 }
141
142
143
144 void InsetFormulaBase::validate(LaTeXFeatures &) const
145 {}
146
147
148 string const InsetFormulaBase::editMessage() const
149 {
150         return _("Math editor mode");
151 }
152
153
154 void InsetFormulaBase::insetUnlock(BufferView * bv)
155 {
156         if (mathcursor) {
157                 if (mathcursor->inMacroMode())
158                         mathcursor->macroModeClose();
159                 releaseMathCursor(bv);
160         }
161         if (bv->buffer())
162                 generatePreview(*bv->buffer());
163         bv->update();
164 }
165
166
167 void InsetFormulaBase::getCursor(BufferView &, int & x, int & y) const
168 {
169         mathcursor->getPos(x, y);
170 }
171
172
173 void InsetFormulaBase::getCursorPos(BufferView *, int & x, int & y) const
174 {
175         if (!mathcursor) {
176                 lyxerr << "getCursorPos - should not happen";
177                 x = 0;
178                 y = 0;
179                 return;
180         }
181         mathcursor->getPos(x, y);
182         x = mathcursor->targetX();
183         x -= xo_;
184         y -= yo_;
185         lyxerr << "InsetFormulaBase::getCursorPos: " << x << ' ' << y << endl;
186 }
187
188
189 void InsetFormulaBase::fitInsetCursor(BufferView * bv) const
190 {
191         if (!mathcursor)
192                 return;
193         int x, y, asc, des;
194         asc = 10;
195         des = 2;
196         //math_font_max_dim(font_, asc, des);
197         getCursorPos(bv, x, y);
198         //y += yo_;
199         //lyxerr << "fitInsetCursor: x: " << x << " y: " << y << " yo: " << yo_ << endl;
200         bv->fitLockedInsetCursor(x, y, asc, des);
201 }
202
203
204 void InsetFormulaBase::toggleInsetSelection(BufferView * bv)
205 {
206         if (mathcursor)
207                 bv->update();
208 }
209
210
211 DispatchResult InsetFormulaBase::lfunMouseRelease(FuncRequest const & cmd)
212 {
213         if (!mathcursor)
214                 return DispatchResult(false);
215
216         BufferView * bv = cmd.view();
217         bv->update();
218         //lyxerr << "lfunMouseRelease: buttons: " << cmd.button() << endl;
219
220         if (cmd.button() == mouse_button::button3) {
221                 // try to dispatch to enclosed insets first
222                 if (!mathcursor->dispatch(cmd).dispatched()) {
223                         // launch math panel for right mouse button
224                         lyxerr << "lfunMouseRelease: undispatched: " << cmd.button() << endl;
225                         bv->owner()->getDialogs().show("mathpanel");
226                 }
227                 return DispatchResult(true, true);
228         }
229
230         if (cmd.button() == mouse_button::button2) {
231                 MathArray ar;
232                 asArray(bv->getClipboard(), ar);
233                 mathcursor->selClear();
234                 mathcursor->setPos(cmd.x + xo_, cmd.y + yo_);
235                 mathcursor->insert(ar);
236                 bv->update();
237                 return DispatchResult(true, true);
238         }
239
240         if (cmd.button() == mouse_button::button1) {
241                 // try to dispatch to enclosed insets first
242                 mathcursor->dispatch(cmd);
243                 cmd.view()->stuffClipboard(mathcursor->grabSelection());
244                 // try to set the cursor
245                 //delete mathcursor;
246                 //mathcursor = new MathCursor(this, x == 0);
247                 //metrics(bv);
248                 //mathcursor->setPos(x + xo_, y + yo_);
249                 return DispatchResult(true, true);
250         }
251
252         return DispatchResult(false);
253 }
254
255
256 DispatchResult InsetFormulaBase::lfunMousePress(FuncRequest const & cmd)
257 {
258         BufferView * bv = cmd.view();
259         //lyxerr << "lfunMousePress: buttons: " << cmd.button() << endl;
260
261         if (!mathcursor || mathcursor->formula() != this) {
262                 lyxerr[Debug::MATHED] << "re-create cursor" << endl;
263                 releaseMathCursor(bv);
264                 mathcursor = new MathCursor(this, cmd.x == 0);
265                 //metrics(bv);
266                 mathcursor->setPos(cmd.x + xo_, cmd.y + yo_);
267         }
268
269         if (cmd.button() == mouse_button::button3) {
270                 mathcursor->dispatch(cmd);
271                 return DispatchResult(true, true);
272         }
273
274         if (cmd.button() == mouse_button::button1) {
275                 first_x = cmd.x;
276                 first_y = cmd.y;
277                 mathcursor->selClear();
278                 mathcursor->setPos(cmd.x + xo_, cmd.y + yo_);
279                 mathcursor->dispatch(cmd);
280                 return DispatchResult(true, true);
281         }
282
283         bv->update();
284         return DispatchResult(true, true);
285 }
286
287
288 DispatchResult InsetFormulaBase::lfunMouseMotion(FuncRequest const & cmd)
289 {
290         if (!mathcursor)
291                 return DispatchResult(true, true);
292
293         if (mathcursor->dispatch(FuncRequest(cmd)).dispatched())
294                 return DispatchResult(true, true);
295
296         // only select with button 1
297         if (cmd.button() != mouse_button::button1)
298                 return DispatchResult(true, true);
299
300         if (abs(cmd.x - first_x) < 2 && abs(cmd.y - first_y) < 2)
301                 return DispatchResult(true, true);
302
303         first_x = cmd.x;
304         first_y = cmd.y;
305
306         if (!mathcursor->selection())
307                 mathcursor->selStart();
308
309         BufferView * bv = cmd.view();
310         mathcursor->setPos(cmd.x + xo_, cmd.y + yo_);
311         bv->update();
312         return DispatchResult(true, true);
313 }
314
315
316 void InsetFormulaBase::edit(BufferView * bv, bool left)
317 {
318         lyxerr << "Called FormulaBase::edit" << endl;
319         releaseMathCursor(bv);
320         mathcursor = new MathCursor(this, left);
321         bv->cursor().push(this); 
322         // if that is removed, we won't get the magenta box when entering an
323         // inset for the first time
324         bv->update();
325 }
326
327
328 void InsetFormulaBase::edit(BufferView * bv, int x, int y)
329 {
330         lyxerr << "Called FormulaBase::EDIT with '" << x << ' ' << y << "'" << endl;
331         releaseMathCursor(bv);
332         mathcursor = new MathCursor(this, true);
333         //metrics(bv);
334         mathcursor->setPos(x + xo_, y + yo_);
335         bv->cursor().push(this); 
336         // if that is removed, we won't get the magenta box when entering an
337         // inset for the first time
338         bv->update();
339 }
340
341
342 DispatchResult
343 InsetFormulaBase::priv_dispatch(FuncRequest const & cmd,
344                                 idx_type &, pos_type &)
345 {
346         //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
347         //      << " arg: '" << cmd.argument
348         //      << "' x: '" << cmd.x
349         //      << " y: '" << cmd.y
350         //      << "' button: " << cmd.button() << endl;
351
352         BufferView * bv = cmd.view();
353
354         // delete empty mathbox (LFUN_BACKSPACE and LFUN_DELETE)
355         bool remove_inset = false;
356
357         switch (cmd.action) {
358                 case LFUN_MOUSE_PRESS:
359                         //lyxerr << "Mouse single press" << endl;
360                         return lfunMousePress(cmd);
361                 case LFUN_MOUSE_MOTION:
362                         //lyxerr << "Mouse motion" << endl;
363                         return lfunMouseMotion(cmd);
364                 case LFUN_MOUSE_RELEASE:
365                         //lyxerr << "Mouse single release" << endl;
366                         return lfunMouseRelease(cmd);
367                 case LFUN_MOUSE_DOUBLE:
368                         //lyxerr << "Mouse double" << endl;
369                         return dispatch(FuncRequest(LFUN_WORDSEL));
370                 default:
371                         break;
372         }
373
374         if (!mathcursor)
375                 return DispatchResult(false);
376
377         string argument    = cmd.argument;
378         DispatchResult result(true);
379         bool sel           = false;
380         bool was_macro     = mathcursor->inMacroMode();
381         bool was_selection = mathcursor->selection();
382
383         mathcursor->normalize();
384         mathcursor->touch();
385
386         switch (cmd.action) {
387
388         case LFUN_MATH_MUTATE:
389         case LFUN_MATH_DISPLAY:
390         case LFUN_MATH_NUMBER:
391         case LFUN_MATH_NONUMBER:
392         case LFUN_CELL_SPLIT:
393         case LFUN_BREAKLINE:
394         case LFUN_DELETE_LINE_FORWARD:
395         case LFUN_INSERT_LABEL:
396         case LFUN_MATH_EXTERN:
397         case LFUN_TABULAR_FEATURE:
398         case LFUN_PASTESELECTION:
399         case LFUN_MATH_LIMITS:
400                 recordUndo(bv, Undo::ATOMIC);
401                 mathcursor->dispatch(cmd);
402                 break;
403
404         case LFUN_RIGHTSEL:
405                 sel = true; // fall through...
406         case LFUN_RIGHT:
407                 result = mathcursor->right(sel) ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
408                 //lyxerr << "calling scroll 20" << endl;
409                 //scroll(bv, 20);
410                 // write something to the minibuffer
411                 //bv->owner()->message(mathcursor->info());
412                 break;
413
414         case LFUN_LEFTSEL:
415                 sel = true; // fall through
416         case LFUN_LEFT:
417                 result = mathcursor->left(sel) ? DispatchResult(true, true) : DispatchResult(true, FINISHED);
418                 break;
419
420         case LFUN_UPSEL:
421                 sel = true; // fall through
422         case LFUN_UP:
423                 result = mathcursor->up(sel) ? DispatchResult(true, true) : DispatchResult(false, FINISHED_UP);
424                 break;
425
426         case LFUN_DOWNSEL:
427                 sel = true; // fall through
428         case LFUN_DOWN:
429                 result = mathcursor->down(sel) ? DispatchResult(true, true) : DispatchResult(false, FINISHED_DOWN);
430                 break;
431
432         case LFUN_WORDSEL:
433                 mathcursor->home(false);
434                 mathcursor->end(true);
435                 break;
436
437         case LFUN_UP_PARAGRAPHSEL:
438         case LFUN_UP_PARAGRAPH:
439         case LFUN_DOWN_PARAGRAPHSEL:
440         case LFUN_DOWN_PARAGRAPH:
441                 result = DispatchResult(true, FINISHED);
442                 break;
443
444         case LFUN_HOMESEL:
445         case LFUN_WORDLEFTSEL:
446                 sel = true; // fall through
447         case LFUN_HOME:
448         case LFUN_WORDLEFT:
449                 result = mathcursor->home(sel) ? DispatchResult(true, true) : DispatchResult(true, FINISHED);
450                 break;
451
452         case LFUN_ENDSEL:
453         case LFUN_WORDRIGHTSEL:
454                 sel = true; // fall through
455         case LFUN_END:
456         case LFUN_WORDRIGHT:
457                 result = mathcursor->end(sel) ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
458                 break;
459
460         case LFUN_PRIORSEL:
461         case LFUN_PRIOR:
462         case LFUN_BEGINNINGBUFSEL:
463         case LFUN_BEGINNINGBUF:
464                 result = DispatchResult(true, FINISHED);
465                 break;
466
467         case LFUN_NEXTSEL:
468         case LFUN_NEXT:
469         case LFUN_ENDBUFSEL:
470         case LFUN_ENDBUF:
471                 result = DispatchResult(false, FINISHED_RIGHT);
472                 break;
473
474         case LFUN_CELL_FORWARD:
475                 mathcursor->idxNext();
476                 break;
477
478         case LFUN_CELL_BACKWARD:
479                 mathcursor->idxPrev();
480                 break;
481
482         case LFUN_DELETE_WORD_BACKWARD:
483         case LFUN_BACKSPACE:
484                 recordUndo(bv, Undo::ATOMIC);
485                 if (!mathcursor->backspace()) {
486                         result = DispatchResult(true, FINISHED);
487                         remove_inset = true;
488                 }
489                 break;
490
491         case LFUN_DELETE_WORD_FORWARD:
492         case LFUN_DELETE:
493                 recordUndo(bv, Undo::ATOMIC);
494                 if (!mathcursor->erase()) {
495                         result = DispatchResult(true, FINISHED);
496                         remove_inset = true;
497                 }
498                 break;
499
500         //    case LFUN_GETXY:
501         //      sprintf(dispatch_buffer, "%d %d",);
502         //      DispatchResult= dispatch_buffer;
503         //      break;
504         case LFUN_SETXY: {
505                 lyxerr << "LFUN_SETXY broken!" << endl;
506                 int x = 0;
507                 int y = 0;
508                 istringstream is(cmd.argument.c_str());
509                 is >> x >> y;
510                 mathcursor->setPos(x, y);
511                 break;
512         }
513
514         case LFUN_PASTE: {
515                 size_t n = 0;
516                 istringstream is(cmd.argument.c_str());
517                 is >> n;
518                 if (was_macro)
519                         mathcursor->macroModeClose();
520                 recordUndo(bv, Undo::ATOMIC);
521                 mathcursor->selPaste(n);
522                 break;
523         }
524
525         case LFUN_CUT:
526                 recordUndo(bv, Undo::DELETE);
527                 mathcursor->selCut();
528                 break;
529
530         case LFUN_COPY:
531                 mathcursor->selCopy();
532                 break;
533
534
535         // Special casing for superscript in case of LyX handling
536         // dead-keys:
537         case LFUN_CIRCUMFLEX:
538                 if (cmd.argument.empty()) {
539                         // do superscript if LyX handles
540                         // deadkeys
541                         recordUndo(bv, Undo::ATOMIC);
542                         mathcursor->script(true);
543                 }
544                 break;
545
546         case LFUN_UMLAUT:
547         case LFUN_ACUTE:
548         case LFUN_GRAVE:
549         case LFUN_BREVE:
550         case LFUN_DOT:
551         case LFUN_MACRON:
552         case LFUN_CARON:
553         case LFUN_TILDE:
554         case LFUN_CEDILLA:
555         case LFUN_CIRCLE:
556         case LFUN_UNDERDOT:
557         case LFUN_TIE:
558         case LFUN_OGONEK:
559         case LFUN_HUNG_UMLAUT:
560                 break;
561
562         //  Math fonts
563         case LFUN_FREEFONT_APPLY:
564         case LFUN_FREEFONT_UPDATE:
565                 handleFont2(bv, cmd.argument);
566                 break;
567
568         case LFUN_BOLD:         handleFont(bv, cmd.argument, "mathbf"); break;
569         case LFUN_SANS:         handleFont(bv, cmd.argument, "mathsf"); break;
570         case LFUN_EMPH:         handleFont(bv, cmd.argument, "mathcal"); break;
571         case LFUN_ROMAN:        handleFont(bv, cmd.argument, "mathrm"); break;
572         case LFUN_CODE:         handleFont(bv, cmd.argument, "texttt"); break;
573         case LFUN_FRAK:         handleFont(bv, cmd.argument, "mathfrak"); break;
574         case LFUN_ITAL:         handleFont(bv, cmd.argument, "mathit"); break;
575         case LFUN_NOUN:         handleFont(bv, cmd.argument, "mathbb"); break;
576         //case LFUN_FREEFONT_APPLY:  handleFont(bv, cmd.argument, "textrm"); break;
577         case LFUN_DEFAULT:      handleFont(bv, cmd.argument, "textnormal"); break;
578
579         case LFUN_MATH_MODE:
580                 if (mathcursor->currentMode() == MathInset::TEXT_MODE)
581                         mathcursor->niceInsert(MathAtom(new MathHullInset("simple")));
582                 else
583                         handleFont(bv, cmd.argument, "textrm");
584                 //bv->owner()->message(_("math text mode toggled"));
585                 break;
586
587         case LFUN_MATH_SIZE:
588 #if 0
589                 if (!arg.empty()) {
590                         recordUndo(bv, Undo::ATOMIC);
591                         mathcursor->setSize(arg);
592                 }
593 #endif
594                 break;
595
596         case LFUN_INSERT_MATRIX: {
597                 recordUndo(bv, Undo::ATOMIC);
598                 unsigned int m = 1;
599                 unsigned int n = 1;
600                 string v_align;
601                 string h_align;
602                 istringstream is(argument);
603                 is >> m >> n >> v_align >> h_align;
604                 m = max(1u, m);
605                 n = max(1u, n);
606                 v_align += 'c';
607                 mathcursor->niceInsert(
608                         MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
609                 break;
610         }
611
612         case LFUN_SUPERSCRIPT:
613         case LFUN_SUBSCRIPT:
614         {
615                 recordUndo(bv, Undo::ATOMIC);
616                 mathcursor->script(cmd.action == LFUN_SUPERSCRIPT);
617                 break;
618         }
619
620         case LFUN_MATH_DELIM:
621         {
622                 //lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'" << endl;
623                 string ls;
624                 string rs = split(cmd.argument, ls, ' ');
625                 // Reasonable default values
626                 if (ls.empty())
627                         ls = '(';
628                 if (rs.empty())
629                         rs = ')';
630
631                 recordUndo(bv, Undo::ATOMIC);
632                 mathcursor->handleNest(MathAtom(new MathDelimInset(ls, rs)));
633                 break;
634         }
635
636         case LFUN_SPACE_INSERT:
637         case LFUN_MATH_SPACE:
638                 recordUndo(bv, Undo::ATOMIC);
639                 mathcursor->insert(MathAtom(new MathSpaceInset(",")));
640                 break;
641
642         case LFUN_UNDO:
643                 bv->owner()->message(_("Invalid action in math mode!"));
644                 break;
645
646
647         case LFUN_EXEC_COMMAND:
648                 result = DispatchResult(false);
649                 break;
650
651         case LFUN_INSET_ERT:
652                 // interpret this as if a backslash was typed
653                 recordUndo(bv, Undo::ATOMIC);
654                 mathcursor->interpret('\\');
655                 break;
656
657         case LFUN_BREAKPARAGRAPH:
658         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
659         case LFUN_BREAKPARAGRAPH_SKIP:
660                 argument = "\n";
661                 // fall through
662
663 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
664 // handling such that "self-insert" works on "arbitrary stuff" too, and
665 // math-insert only handles special math things like "matrix".
666         case LFUN_INSERT_MATH:
667                 recordUndo(bv, Undo::ATOMIC);
668                 mathcursor->niceInsert(argument);
669                 break;
670
671         case -1:
672         case LFUN_SELFINSERT:
673                 if (!argument.empty()) {
674                         recordUndo(bv, Undo::ATOMIC);
675                         if (argument.size() == 1)
676                                 result = mathcursor->interpret(argument[0]) ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
677                         else
678                                 mathcursor->insert(argument);
679                 }
680                 break;
681
682         case LFUN_ESCAPE:
683                 if (mathcursor->selection())
684                         mathcursor->selClear();
685                 else
686                         result = DispatchResult(false);
687                 break;
688
689         case LFUN_INSET_TOGGLE:
690                 mathcursor->insetToggle();
691                 break;
692
693         case LFUN_DIALOG_SHOW:
694                 result = DispatchResult(false);
695                 break;
696
697         case LFUN_DIALOG_SHOW_NEW_INSET: {
698                 string const & name = argument;
699                 string data;
700                 if (name == "ref") {
701                         RefInset tmp(name);
702                         data = tmp.createDialogStr(name);
703                 }
704
705                 if (data.empty())
706                         result = DispatchResult(false);
707                 else
708                         bv->owner()->getDialogs().show(name, data, 0);
709         }
710         break;
711
712         case LFUN_INSET_APPLY: {
713                 string const name = cmd.getArg(0);
714                 InsetBase * base =
715                         bv->owner()->getDialogs().getOpenInset(name);
716
717                 if (base) {
718                         FuncRequest fr(bv, LFUN_INSET_MODIFY, cmd.argument);
719                         result = base->dispatch(fr);
720                 } else {
721                         MathArray ar;
722                         if (createMathInset_fromDialogStr(cmd.argument, ar)) {
723                                 mathcursor->insert(ar);
724                                 result = DispatchResult(true, true);
725                         } else {
726                                 result = DispatchResult(false);
727                         }
728                 }
729         }
730         break;
731
732         default:
733                 result = DispatchResult(false);
734         }
735
736         if (result == DispatchResult(true, true))
737                 bv->update();
738
739         mathcursor->normalize();
740         mathcursor->touch();
741
742         BOOST_ASSERT(mathcursor);
743
744         if (mathcursor->selection() || was_selection)
745                 toggleInsetSelection(bv);
746
747         if (result.dispatched()) {
748                 fitInsetCursor(bv);
749                 revealCodes(bv);
750                 cmd.view()->stuffClipboard(mathcursor->grabSelection());
751         } else {
752                 releaseMathCursor(bv);
753                 if (remove_inset)
754                         bv->owner()->dispatch(FuncRequest(LFUN_DELETE));
755         }
756
757         return result;  // original version
758 }
759
760
761 void InsetFormulaBase::revealCodes(BufferView * bv) const
762 {
763         if (!mathcursor)
764                 return;
765         bv->owner()->message(mathcursor->info());
766
767 #if 0
768         // write something to the minibuffer
769         // translate to latex
770         mathcursor->markInsert();
771         ostringstream os;
772         write(NULL, os);
773         string str = os.str();
774         mathcursor->markErase();
775         string::size_type pos = 0;
776         string res;
777         for (string::iterator it = str.begin(); it != str.end(); ++it) {
778                 if (*it == '\n')
779                         res += ' ';
780                 else if (*it == '\0') {
781                         res += "  -X-  ";
782                         pos = it - str.begin();
783                 }
784                 else
785                         res += *it;
786         }
787         if (pos > 30)
788                 res = res.substr(pos - 30);
789         if (res.size() > 60)
790                 res = res.substr(0, 60);
791         bv->owner()->message(res);
792 #endif
793 }
794
795
796 InsetOld::Code InsetFormulaBase::lyxCode() const
797 {
798         return InsetOld::MATH_CODE;
799 }
800
801
802 int InsetFormulaBase::ylow() const
803 {
804         return yo_ - dim_.asc;
805 }
806
807
808 int InsetFormulaBase::yhigh() const
809 {
810         return yo_ + dim_.des;
811 }
812
813
814 int InsetFormulaBase::xlow() const
815 {
816         return xo_;
817 }
818
819
820 int InsetFormulaBase::xhigh() const
821 {
822         return xo_ + dim_.wid;
823 }
824
825
826 /////////////////////////////////////////////////////////////////////
827
828
829 bool InsetFormulaBase::searchForward(BufferView * bv, string const & str,
830                                      bool, bool)
831 {
832 #ifdef WITH_WARNINGS
833 #warning pretty ugly
834 #endif
835         static InsetFormulaBase * lastformula = 0;
836         static MathIterator current = MathIterator(ibegin(par().nucleus()));
837         static MathArray ar;
838         static string laststr;
839
840         if (lastformula != this || laststr != str) {
841                 //lyxerr << "reset lastformula to " << this << endl;
842                 lastformula = this;
843                 laststr = str;
844                 current = ibegin(par().nucleus());
845                 ar.clear();
846                 mathed_parse_cell(ar, str);
847         } else {
848                 ++current;
849         }
850         //lyxerr << "searching '" << str << "' in " << this << ar << endl;
851
852         for (MathIterator it = current; it != iend(par().nucleus()); ++it) {
853                 if (it.cell().matchpart(ar, it.back().pos_)) {
854                         delete mathcursor;
855                         mathcursor = new MathCursor(this, true);
856                         //metrics(bv);
857                         mathcursor->setSelection(it, ar.size());
858                         current = it;
859                         it.jump(ar.size());
860                         bv->update();
861                         return true;
862                 }
863         }
864
865         //lyxerr << "not found!" << endl;
866         lastformula = 0;
867         return false;
868 }
869
870
871 bool InsetFormulaBase::searchBackward(BufferView * bv, string const & what,
872                                       bool a, bool b)
873 {
874         lyxerr[Debug::MATHED] << "searching backward not implemented in mathed" << endl;
875         return searchForward(bv, what, a, b);
876 }
877
878
879 bool InsetFormulaBase::display() const
880 {
881         return par()->asHullInset() && par()->asHullInset()->display();
882 }
883
884
885 string InsetFormulaBase::selectionAsString() const
886 {
887         return mathcursor ? mathcursor->grabSelection() : string();
888 }
889
890 /////////////////////////////////////////////////////////////////////
891
892
893 void mathDispatchCreation(FuncRequest const & cmd, bool display)
894 {
895         BufferView * bv = cmd.view();
896         // use selection if available..
897         //string sel;
898         //if (action == LFUN_MATH_IMPORT_SELECTION)
899         //      sel = "";
900         //else
901
902         string sel = bv->getLyXText()->selectionAsString(*bv->buffer(), false);
903
904         if (sel.empty()) {
905                 InsetFormula * f = new InsetFormula(bv);
906                 if (openNewInset(bv, f)) {
907                         bv->cursor().innerInset()->
908                                 dispatch(FuncRequest(bv, LFUN_MATH_MUTATE, "simple"));
909                         // don't do that also for LFUN_MATH_MODE unless you want end up with
910                         // always changing to mathrm when opening an inlined inset
911                         // -- I really hate "LyXfunc overloading"...
912                         if (display)
913                                 f->dispatch(FuncRequest(bv, LFUN_MATH_DISPLAY));
914                         f->dispatch(FuncRequest(bv, LFUN_INSERT_MATH, cmd.argument));
915                 }
916         } else {
917                 // create a macro if we see "\\newcommand" somewhere, and an ordinary
918                 // formula otherwise
919                 InsetFormulaBase * f;
920                 if (sel.find("\\newcommand") == string::npos &&
921                                 sel.find("\\def") == string::npos)
922                         f = new InsetFormula(sel);
923                 else
924                         f = new InsetFormulaMacro(sel);
925                 bv->getLyXText()->cutSelection(true, false);
926                 openNewInset(bv, f);
927         }
928         cmd.message(N_("Math editor mode"));
929 }
930
931
932 void mathDispatch(FuncRequest const & cmd)
933 {
934         BufferView * bv = cmd.view();
935         if (!bv->available())
936                 return;
937
938         switch (cmd.action) {
939
940                 case LFUN_MATH_DISPLAY:
941                         mathDispatchCreation(cmd, true);
942                         break;
943
944                 case LFUN_MATH_MODE:
945                         mathDispatchCreation(cmd, false);
946                         break;
947
948                 case LFUN_MATH_IMPORT_SELECTION:
949                         mathDispatchCreation(cmd, false);
950                         break;
951
952                 case LFUN_MATH_MACRO:
953                         if (cmd.argument.empty())
954                                 cmd.errorMessage(N_("Missing argument"));
955                         else {
956                                 string s = cmd.argument;
957                                 string const s1 = token(s, ' ', 1);
958                                 int const nargs = s1.empty() ? 0 : atoi(s1);
959                                 string const s2 = token(s, ' ', 2);
960                                 string const type = s2.empty() ? "newcommand" : s2;
961                                 openNewInset(bv, new InsetFormulaMacro(token(s, ' ', 0), nargs, s2));
962                         }
963                         break;
964
965                 case LFUN_INSERT_MATH:
966                 case LFUN_INSERT_MATRIX:
967                 case LFUN_MATH_DELIM: {
968                         InsetFormula * f = new InsetFormula(bv);
969                         if (openNewInset(bv, f)) {
970                                 UpdatableInset * inset = bv->cursor().innerInset();
971                                 inset->dispatch(FuncRequest(bv, LFUN_MATH_MUTATE, "simple"));
972                                 inset->dispatch(cmd);
973                         }
974                         break;
975                 }
976
977                 default:
978                         break;
979         }
980 }