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