]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathNest.cpp
Force a Buffer * argument to math insets constructor
[lyx.git] / src / mathed / InsetMathNest.cpp
1 /**
2  * \file InsetMathNest.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "InsetMathNest.h"
14
15 #include "InsetMathArray.h"
16 #include "InsetMathAMSArray.h"
17 #include "InsetMathBig.h"
18 #include "InsetMathBox.h"
19 #include "InsetMathBrace.h"
20 #include "InsetMathChar.h"
21 #include "InsetMathColor.h"
22 #include "InsetMathComment.h"
23 #include "InsetMathDecoration.h"
24 #include "InsetMathDelim.h"
25 #include "InsetMathEnsureMath.h"
26 #include "InsetMathFont.h"
27 #include "InsetMathHull.h"
28 #include "InsetMathRef.h"
29 #include "InsetMathScript.h"
30 #include "InsetMathSpace.h"
31 #include "InsetMathSymbol.h"
32 #include "InsetMathUnknown.h"
33 #include "MathAutoCorrect.h"
34 #include "MathCompletionList.h"
35 #include "MathFactory.h"
36 #include "InsetMathMacro.h"
37 #include "InsetMathMacroArgument.h"
38 #include "MathParser.h"
39 #include "MathStream.h"
40 #include "MathSupport.h"
41
42 #include "Buffer.h"
43 #include "BufferParams.h"
44 #include "BufferView.h"
45 #include "CoordCache.h"
46 #include "Cursor.h"
47 #include "CutAndPaste.h"
48 #include "DispatchResult.h"
49 #include "Encoding.h"
50 #include "FuncRequest.h"
51 #include "FuncStatus.h"
52 #include "LaTeXFeatures.h"
53 #include "LyX.h"
54 #include "LyXRC.h"
55 #include "MetricsInfo.h"
56 #include "TexRow.h"
57 #include "Text.h"
58
59 #include "frontends/Application.h"
60 #include "frontends/Clipboard.h"
61 #include "frontends/Painter.h"
62 #include "frontends/Selection.h"
63
64 #include "support/debug.h"
65 #include "support/docstream.h"
66 #include "support/gettext.h"
67 #include "support/lassert.h"
68 #include "support/lstrings.h"
69 #include "support/textutils.h"
70
71 #include <algorithm>
72 #include <sstream>
73
74 using namespace std;
75 using namespace lyx::support;
76
77 namespace lyx {
78
79 using cap::copySelection;
80 using cap::grabAndEraseSelection;
81 using cap::cutSelection;
82 using cap::replaceSelection;
83 using cap::selClearOrDel;
84
85
86 InsetMathNest::InsetMathNest(Buffer * buf, idx_type nargs)
87         : InsetMath(buf), cells_(nargs, MathData(buffer_)), lock_(false)
88 {
89         // FIXME This should not really be necessary, but when we are
90         // initializing the table of global macros, we create macros
91         // with no associated Buffer.
92         if (buf)
93                 setBuffer(*buf);
94 }
95
96
97 InsetMathNest::InsetMathNest(InsetMathNest const & inset)
98         : InsetMath(inset), cells_(inset.cells_), lock_(inset.lock_)
99 {}
100
101
102 InsetMathNest::~InsetMathNest()
103 {
104         map<BufferView const *, bool>::iterator it = mouse_hover_.begin();
105         map<BufferView const *, bool>::iterator end = mouse_hover_.end();
106         for (; it != end; ++it)
107                 if (it->second)
108                         it->first->clearLastInset(this);
109 }
110
111
112 InsetMathNest & InsetMathNest::operator=(InsetMathNest const & inset)
113 {
114         cells_ = inset.cells_;
115         lock_ = inset.lock_;
116         mouse_hover_.clear();
117         InsetMath::operator=(inset);
118         return *this;
119 }
120
121
122 void InsetMathNest::setBuffer(Buffer & buffer)
123 {
124         InsetMath::setBuffer(buffer);
125         for (MathData & data : cells_)
126                 data.setBuffer(buffer);
127 }
128
129
130 idx_type InsetMathNest::nargs() const
131 {
132         return cells_.size();
133 }
134
135
136 void InsetMathNest::cursorPos(BufferView const & bv,
137                 CursorSlice const & sl, bool /*boundary*/,
138                 int & x, int & y) const
139 {
140 // FIXME: This is a hack. Ideally, the coord cache should not store
141 // absolute positions, but relative ones. This would mean to call
142 // setXY() not in MathData::draw(), but in the parent insets' draw()
143 // with the correctly adjusted x,y values. But this means that we'd have
144 // to touch all (math)inset's draw() methods. Right now, we'll store
145 // absolute value, and make them here relative, only to make them
146 // absolute again when actually drawing the cursor. What a mess.
147         LASSERT(&sl.inset() == this, return);
148         MathData const & ar = sl.cell();
149         CoordCache const & coord_cache = bv.coordCache();
150         if (!coord_cache.getArrays().has(&ar)) {
151                 // this can (semi-)legally happen if we just created this cell
152                 // and it never has been drawn before. So don't ASSERT.
153                 //lyxerr << "no cached data for array " << &ar << endl;
154                 x = 0;
155                 y = 0;
156                 return;
157         }
158         Point const pt = coord_cache.getArrays().xy(&ar);
159         if (!coord_cache.getInsets().has(this)) {
160                 // same as above
161                 //lyxerr << "no cached data for inset " << this << endl;
162                 x = 0;
163                 y = 0;
164                 return;
165         }
166         Point const pt2 = coord_cache.getInsets().xy(this);
167         //lyxerr << "retrieving position cache for MathData "
168         //      << pt.x_ << ' ' << pt.y_ << endl;
169         x = pt.x_ - pt2.x_ + ar.pos2x(&bv, sl.pos());
170         y = pt.y_ - pt2.y_;
171 //      lyxerr << "pt.y_ : " << pt.y_ << " pt2_.y_ : " << pt2.y_
172 //              << " asc: " << ascent() << "  des: " << descent()
173 //              << " ar.asc: " << ar.ascent() << " ar.des: " << ar.descent() << endl;
174         // move cursor visually into empty cells ("blue rectangles");
175         if (ar.empty()) {
176                 Dimension const dim = coord_cache.getArrays().dim(&ar);
177                 x += dim.wid / 3;
178         }
179 }
180
181
182 void InsetMathNest::cellsMetrics(MetricsInfo const & mi) const
183 {
184         MetricsInfo m = mi;
185         for (auto const & cell : cells_) {
186                 Dimension dim;
187                 cell.metrics(m, dim);
188         }
189 }
190
191
192 void InsetMathNest::updateBuffer(ParIterator const & it, UpdateType utype, bool const deleted)
193 {
194         for (idx_type i = 0, n = nargs(); i != n; ++i)
195                 cell(i).updateBuffer(it, utype, deleted);
196 }
197
198
199
200 bool InsetMathNest::idxNext(Cursor & cur) const
201 {
202         LASSERT(&cur.inset() == this, return false);
203         if (cur.idx() == cur.lastidx())
204                 return false;
205         ++cur.idx();
206         cur.pos() = 0;
207         return true;
208 }
209
210
211 bool InsetMathNest::idxForward(Cursor & cur) const
212 {
213         return idxNext(cur);
214 }
215
216
217 bool InsetMathNest::idxPrev(Cursor & cur) const
218 {
219         LASSERT(&cur.inset() == this, return false);
220         if (cur.idx() == 0)
221                 return false;
222         --cur.idx();
223         cur.pos() = lyxrc.mac_like_cursor_movement ? cur.lastpos() : 0;
224         return true;
225 }
226
227
228 bool InsetMathNest::idxBackward(Cursor & cur) const
229 {
230         return idxPrev(cur);
231 }
232
233
234 bool InsetMathNest::idxFirst(Cursor & cur) const
235 {
236         LASSERT(&cur.inset() == this, return false);
237         if (nargs() == 0)
238                 return false;
239         cur.idx() = firstIdx();
240         cur.pos() = 0;
241         return true;
242 }
243
244
245 bool InsetMathNest::idxLast(Cursor & cur) const
246 {
247         LASSERT(&cur.inset() == this, return false);
248         if (nargs() == 0)
249                 return false;
250         cur.idx() = lastIdx();
251         cur.pos() = cur.lastpos();
252         return true;
253 }
254
255
256 void InsetMathNest::dump() const
257 {
258         odocstringstream oss;
259         otexrowstream ots(oss);
260         TeXMathStream os(ots);
261         os << "---------------------------------------------\n";
262         write(os);
263         os << "\n";
264         for (idx_type i = 0, n = nargs(); i != n; ++i)
265                 os << cell(i) << "\n";
266         os << "---------------------------------------------\n";
267         lyxerr << to_utf8(oss.str());
268 }
269
270
271 void InsetMathNest::draw(PainterInfo &, int, int) const
272 {
273 #if 0
274         if (lock_)
275                 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
276                                         Color_mathlockbg);
277 #endif
278 }
279
280
281 void InsetMathNest::validate(LaTeXFeatures & features) const
282 {
283         for (idx_type i = 0; i < nargs(); ++i)
284                 cell(i).validate(features);
285 }
286
287
288 void InsetMathNest::replace(ReplaceData & rep)
289 {
290         for (idx_type i = 0; i < nargs(); ++i)
291                 cell(i).replace(rep);
292 }
293
294
295 bool InsetMathNest::contains(MathData const & ar) const
296 {
297         for (idx_type i = 0; i < nargs(); ++i)
298                 if (cell(i).contains(ar))
299                         return true;
300         return false;
301 }
302
303
304 bool InsetMathNest::lock() const
305 {
306         return lock_;
307 }
308
309
310 void InsetMathNest::lock(bool l)
311 {
312         lock_ = l;
313 }
314
315
316 bool InsetMathNest::isActive() const
317 {
318         return nargs() > 0;
319 }
320
321
322 MathData InsetMathNest::glue() const
323 {
324         MathData ar(buffer_);
325         for (size_t i = 0; i < nargs(); ++i)
326                 ar.append(cell(i));
327         return ar;
328 }
329
330
331 void InsetMathNest::write(TeXMathStream & os) const
332 {
333         MathEnsurer ensurer(os, currentMode() == MATH_MODE);
334         ModeSpecifier specifier(os, currentMode(), lockedMode());
335         docstring const latex_name = name();
336         os << '\\' << latex_name;
337         os.inMathClass(asClassInset());
338         for (size_t i = 0; i < nargs(); ++i) {
339                 Changer dummy = os.changeRowEntry(TexRow::mathEntry(id(),i));
340                 os << '{' << cell(i) << '}';
341         }
342         if (nargs() == 0)
343                 os.pendingSpace(true);
344         if (lock_ && !os.latex()) {
345                 os << "\\lyxlock";
346                 os.pendingSpace(true);
347         }
348         os.inMathClass(false);
349 }
350
351
352 void InsetMathNest::normalize(NormalStream & os) const
353 {
354         os << '[' << name();
355         for (size_t i = 0; i < nargs(); ++i)
356                 os << ' ' << cell(i);
357         os << ']';
358 }
359
360
361 void InsetMathNest::latex(otexstream & os, OutputParams const & runparams) const
362 {
363         TeXMathStream::OutputType ot;
364         if (runparams.find_effective())
365                 ot = TeXMathStream::wsSearchAdv;
366         else if (runparams.dryrun)
367                 ot = TeXMathStream::wsDryrun;
368         else
369                 ot = TeXMathStream::wsDefault;
370         TeXMathStream wi(os, runparams.moving_arg, true, ot,
371                          runparams.encoding);
372         wi.strikeoutMath(runparams.inDeletedInset);
373         if (runparams.inulemcmd) {
374                 wi.ulemCmd(TeXMathStream::UNDERLINE);
375                 if (runparams.local_font) {
376                         FontInfo f = runparams.local_font->fontInfo();
377                         if (f.strikeout() == FONT_ON)
378                                 wi.ulemCmd(TeXMathStream::STRIKEOUT);
379                 }
380         }
381         wi.canBreakLine(os.canBreakLine());
382         Changer dummy = wi.changeRowEntry(TexRow::textEntry(runparams.lastid,
383                                                             runparams.lastpos));
384         write(wi);
385         // Reset parbreak and command termination status after a math inset.
386         os.lastChar(0);
387         os.canBreakLine(wi.canBreakLine());
388         os.terminateCommand(false);
389 }
390
391
392 bool InsetMathNest::setMouseHover(BufferView const * bv, bool mouse_hover)
393         const
394 {
395         mouse_hover_[bv] = mouse_hover;
396         return true;
397 }
398
399
400 bool InsetMathNest::notifyCursorLeaves(Cursor const & /*old*/, Cursor & /*cur*/)
401 {
402         // FIXME: look here
403 #if 0
404         MathData & ar = cur.cell();
405         // remove base-only "scripts"
406         for (pos_type i = 0; i + 1 < ar.size(); ++i) {
407                 InsetMathScript * p = operator[](i).nucleus()->asScriptInset();
408                 if (p && p->nargs() == 1) {
409                         MathData ar = p->nuc();
410                         erase(i);
411                         insert(i, ar);
412                         cur.adjust(i, ar.size() - 1);
413                 }
414         }
415
416         // glue adjacent font insets of the same kind
417         for (pos_type i = 0; i + 1 < size(); ++i) {
418                 InsetMathFont * p = operator[](i).nucleus()->asFontInset();
419                 InsetMathFont const * q = operator[](i + 1)->asFontInset();
420                 if (p && q && p->name() == q->name()) {
421                         p->cell(0).append(q->cell(0));
422                         erase(i + 1);
423                         cur.adjust(i, -1);
424                 }
425         }
426 #endif
427         return false;
428 }
429
430
431 void InsetMathNest::handleFont
432         (Cursor & cur, docstring const & arg, char const * const font)
433 {
434         handleFont(cur, arg, from_ascii(font));
435 }
436
437
438 void InsetMathNest::handleFont(Cursor & cur, docstring const & arg,
439         docstring const & font)
440 {
441         cur.recordUndoSelection();
442
443         // this whole function is a hack and won't work for incremental font
444         // changes...
445         if (cur.inset().asInsetMath()->name() == font)
446                 cur.handleFont(to_utf8(font));
447         else
448                 handleNest(cur, createInsetMath(font, cur.buffer()), arg);
449 }
450
451
452 void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest)
453 {
454         handleNest(cur, nest, docstring());
455 }
456
457
458 void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest,
459         docstring const & arg)
460 {
461         DocIterator const i1 = cur.selectionBegin();
462         DocIterator const i2 = cur.selectionEnd();
463         if (!i1.inset().asInsetMath())
464                 return;
465         if (i1.idx() == i2.idx()) {
466                 // the easy case where only one cell is selected
467                 cur.handleNest(nest);
468                 cur.insert(arg);
469                 return;
470         }
471
472         // multiple selected cells in a simple non-grid inset
473         if (i1.inset().nrows() == 0 || i1.inset().ncols() == 0) {
474                 for (idx_type i = i1.idx(); i <= i2.idx(); ++i) {
475                         cur.setCursor(i1);
476                         // select cell
477                         cur.idx() = i;
478                         cur.pos() = 0;
479                         cur.resetAnchor();
480                         cur.pos() = cur.lastpos();
481                         cur.setSelection();
482
483                         // do the real job
484                         cur.handleNest(nest);
485                         cur.insert(arg);
486                 }
487                 return;
488         }
489
490         // the complicated case with multiple selected cells in a grid
491         row_type r1, r2;
492         col_type c1, c2;
493         cap::region(i1.top(), i2.top(), r1, r2, c1, c2);
494         for (row_type row = r1; row <= r2; ++row) {
495                 for (col_type col = c1; col <= c2; ++col) {
496                         cur.setCursor(i1);
497                         // select cell
498                         cur.idx() = i1.inset().index(row, col);
499                         cur.pos() = 0;
500                         cur.resetAnchor();
501                         cur.pos() = cur.lastpos();
502                         cur.setSelection();
503
504                         // do the real job
505                         cur.handleNest(nest);
506                         cur.insert(arg);
507                 }
508         }
509 }
510
511
512 void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg)
513 {
514         cur.recordUndoSelection();
515         bool include_previous_change = false;
516         bool selection = cur.selection();
517         DocIterator sel_begin = cur.selectionBegin();
518         DocIterator sel_end = cur.selectionEnd();
519         bool multiple_cells = sel_begin.idx() != sel_end.idx();
520         Font font;
521         bool b;
522         font.fromString(to_utf8(arg), b);
523         docstring im;
524         InsetMathFont const * f = asFontInset();
525
526         switch(font.fontInfo().family()) {
527         case ROMAN_FAMILY:
528                 if (!f || (f->name() != "textrm" && f->name() != "mathrm"))
529                         im = currentMode() != MATH_MODE ? from_ascii("textrm")
530                                                         : from_ascii("mathrm");
531                 break;
532         case SANS_FAMILY:
533                 if (!f || (f->name() != "textsf" && f->name() != "mathsf"))
534                         im = currentMode() != MATH_MODE ? from_ascii("textsf")
535                                                         : from_ascii("mathsf");
536                 break;
537         case TYPEWRITER_FAMILY:
538                 if (!f || (f->name() != "texttt" && f->name() != "mathtt"))
539                         im = currentMode() != MATH_MODE ? from_ascii("texttt")
540                                                         : from_ascii("mathtt");
541                 break;
542         case SYMBOL_FAMILY:
543         case CMR_FAMILY:
544         case CMSY_FAMILY:
545         case CMM_FAMILY:
546         case CMEX_FAMILY:
547         case MSA_FAMILY:
548         case MSB_FAMILY:
549         case DS_FAMILY:
550         case EUFRAK_FAMILY:
551         case RSFS_FAMILY:
552         case STMARY_FAMILY:
553         case WASY_FAMILY:
554         case ESINT_FAMILY:
555         case INHERIT_FAMILY:
556         case IGNORE_FAMILY:
557                 break;
558         }
559         if (!im.empty()) {
560                 handleNest(cur, createInsetMath(im, cur.buffer()));
561                 im.clear();
562                 include_previous_change = true;
563         }
564
565         switch(font.fontInfo().series()) {
566         case MEDIUM_SERIES:
567                 if (!f || (f->name() != "textmd" && f->name() != "mathrm"))
568                         im = currentMode() != MATH_MODE ? from_ascii("textmd")
569                                                         : from_ascii("mathrm");
570                 break;
571         case BOLD_SERIES:
572                 if (!f || (f->name() != "textbf" && f->name() != "mathbf"))
573                         im = currentMode() != MATH_MODE ? from_ascii("textbf")
574                                                         : from_ascii("boldsymbol");
575                 break;
576         case INHERIT_SERIES:
577         case IGNORE_SERIES:
578                 break;
579         }
580         if (!im.empty()) {
581                 if (multiple_cells) {
582                         cur.setCursor(sel_begin);
583                         cur.idx() = 0;
584                         cur.pos() = 0;
585                         cur.resetAnchor();
586                         cur.setCursor(sel_end);
587                         cur.pos() = cur.lastpos();
588                         cur.setSelection();
589                 } else if (include_previous_change && selection) {
590                         cur.setSelection();
591                 }
592                 handleNest(cur, createInsetMath(im, cur.buffer()));
593                 im.clear();
594                 include_previous_change = true;
595         }
596
597         switch(font.fontInfo().shape()) {
598         case UP_SHAPE:
599                 if (!f || (f->name() != "textup" && f->name() != "mathrm"))
600                         im = currentMode() != MATH_MODE ? from_ascii("textup")
601                                                         : from_ascii("mathrm");
602                 break;
603         case ITALIC_SHAPE:
604                 if (!f || (f->name() != "textit" && f->name() != "mathit"))
605                         im = currentMode() != MATH_MODE ? from_ascii("textit")
606                                                         : from_ascii("mathit");
607                 break;
608         case SLANTED_SHAPE:
609                 if (!f || f->name() != "textsl")
610                         im = currentMode() != MATH_MODE ? from_ascii("textsl")
611                                                         : docstring();
612                 break;
613         case SMALLCAPS_SHAPE:
614                 if (!f || f->name() != "textsc")
615                         im = currentMode() != MATH_MODE ? from_ascii("textsc")
616                                                         : docstring();
617                 break;
618         case INHERIT_SHAPE:
619         case IGNORE_SHAPE:
620                 break;
621         }
622         if (!im.empty()) {
623                 if (multiple_cells) {
624                         cur.setCursor(sel_begin);
625                         cur.idx() = 0;
626                         cur.pos() = 0;
627                         cur.resetAnchor();
628                         cur.setCursor(sel_end);
629                         cur.pos() = cur.lastpos();
630                         cur.setSelection();
631                 } else if (include_previous_change && selection) {
632                         cur.setSelection();
633                 }
634                 handleNest(cur, createInsetMath(im, cur.buffer()));
635                 im.clear();
636                 include_previous_change = true;
637         }
638
639         switch(font.fontInfo().size()) {
640         case TINY_SIZE:
641                 im = from_ascii("tiny");
642                 break;
643         case SCRIPT_SIZE:
644                 im = from_ascii("scriptsize");
645                 break;
646         case FOOTNOTE_SIZE:
647                 im = from_ascii("footnotesize");
648                 break;
649         case SMALL_SIZE:
650                 im = from_ascii("small");
651                 break;
652         case NORMAL_SIZE:
653                 im = from_ascii("normalsize");
654                 break;
655         case LARGE_SIZE:
656                 im = from_ascii("large");
657                 break;
658         case LARGER_SIZE:
659                 im = from_ascii("Large");
660                 break;
661         case LARGEST_SIZE:
662                 im = from_ascii("LARGE");
663                 break;
664         case HUGE_SIZE:
665                 im = from_ascii("huge");
666                 break;
667         case HUGER_SIZE:
668                 im = from_ascii("Huge");
669                 break;
670         case INCREASE_SIZE:
671         case DECREASE_SIZE:
672         case INHERIT_SIZE:
673         case IGNORE_SIZE:
674                 break;
675         }
676         if (!im.empty()) {
677                 if (multiple_cells) {
678                         cur.setCursor(sel_begin);
679                         cur.idx() = 0;
680                         cur.pos() = 0;
681                         cur.resetAnchor();
682                         cur.setCursor(sel_end);
683                         cur.pos() = cur.lastpos();
684                         cur.setSelection();
685                 } else if (include_previous_change && selection) {
686                         cur.setSelection();
687                 }
688                 handleNest(cur, createInsetMath(im, cur.buffer()));
689                 im.clear();
690                 include_previous_change = true;
691         }
692
693         InsetMathDecoration const * d = multiple_cells
694                                         ? nullptr : asDecorationInset();
695         docstring const name = d ? d->name() : docstring();
696
697         if ((font.fontInfo().underbar() == FONT_OFF && name == "uline") ||
698             (font.fontInfo().uuline() == FONT_OFF && name == "uuline") ||
699             (font.fontInfo().uwave() == FONT_OFF && name == "uwave")) {
700                 if (include_previous_change) {
701                         if (!selection)
702                                 cur.popForward();
703                         cur.setSelection();
704                 }
705                 docstring const beg = '\\' + name + '{';
706                 docstring const end = from_ascii("}");
707                 docstring const sel2 = cur.selectionAsString(false);
708                 cutSelection(cur, false);
709                 cur.pos() = 0;
710                 cur.setSelection();
711                 docstring const sel1 = cur.selectionAsString(false);
712                 cur.pos() = cur.lastpos();
713                 cur.setSelection();
714                 docstring const sel3 = cur.selectionAsString(false);
715                 cur.mathForward(false);
716                 cur.setSelection();
717                 cutSelection(cur, false);
718                 MathData ar(buffer_);
719                 if (!sel1.empty()) {
720                         mathed_parse_cell(ar, beg + sel1 + end);
721                         cur.insert(ar);
722                 }
723                 cur.resetAnchor();
724                 if (!sel2.empty()) {
725                         mathed_parse_cell(ar, sel2);
726                         cur.insert(ar);
727                 }
728                 if (!sel3.empty()) {
729                         pos_type pos = cur.pos();
730                         mathed_parse_cell(ar, beg + sel3 + end);
731                         cur.insert(ar);
732                         cur.pos() = pos;
733                 }
734                 cur.setSelection();
735                 sel_begin = cur.selectionBegin();
736                 sel_end = cur.selectionEnd();
737                 include_previous_change = false;
738         }
739
740         if (font.fontInfo().underbar() == FONT_ON) {
741                 if (!d || name != "uline")
742                         im = from_ascii("uline");
743         } else if (font.fontInfo().uuline() == FONT_ON) {
744                 if (!d || name != "uuline")
745                         im = from_ascii("uuline");
746         } else if (font.fontInfo().uwave() == FONT_ON) {
747                 if (!d || name != "uwave")
748                         im = from_ascii("uwave");
749         }
750
751         if (!im.empty()) {
752                 if (multiple_cells) {
753                         cur.setCursor(sel_begin);
754                         cur.idx() = 0;
755                         cur.pos() = 0;
756                         cur.resetAnchor();
757                         cur.setCursor(sel_end);
758                         cur.pos() = cur.lastpos();
759                         cur.setSelection();
760                 } else if (include_previous_change && selection) {
761                         cur.setSelection();
762                 }
763                 handleNest(cur, createInsetMath(im, cur.buffer()));
764                 im.clear();
765                 include_previous_change = true;
766         }
767
768         if (font.fontInfo().color() != Color_inherit &&
769             font.fontInfo().color() != Color_ignore) {
770                 if (multiple_cells) {
771                         cur.setCursor(sel_begin);
772                         cur.idx() = 0;
773                         cur.pos() = 0;
774                         cur.resetAnchor();
775                         cur.setCursor(sel_end);
776                         cur.pos() = cur.lastpos();
777                         cur.setSelection();
778                 } else if (include_previous_change && selection) {
779                         cur.setSelection();
780                 }
781                 handleNest(cur, MathAtom(new InsetMathColor(buffer_, true, font.fontInfo().color())));
782                 include_previous_change = true;
783         }
784
785         if (selection) {
786                 if (multiple_cells) {
787                         cur.setCursor(sel_begin);
788                         cur.idx() = 0;
789                         cur.pos() = 0;
790                         cur.resetAnchor();
791                         cur.setCursor(sel_end);
792                         cur.pos() = cur.lastpos();
793                         cur.setSelection();
794                 } else {
795                         if (include_previous_change) {
796                                 sel_end = cur;
797                                 cur.backwardInset();
798                         } else {
799                                 cur.setCursor(sel_begin);
800                         }
801                         cur.resetAnchor();
802                         cur.setCursor(sel_end);
803                         cur.setSelection();
804                 }
805         }
806
807         // FIXME: support other font changes here as well?
808 }
809
810 void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
811 {
812         //LYXERR0("InsetMathNest: request: " << cmd);
813
814         Parse::flags parseflg = Parse::QUIET | Parse::USETEXT;
815
816         FuncCode const act = cmd.action();
817         switch (act) {
818
819         case LFUN_CLIPBOARD_PASTE:
820                 parseflg |= Parse::VERBATIM;
821                 // fall through
822         case LFUN_PASTE: {
823                 if (cur.currentMode() != MATH_MODE)
824                         parseflg |= Parse::TEXTMODE;
825                 cur.recordUndoSelection();
826                 cur.message(_("Paste"));
827                 replaceSelection(cur);
828                 docstring topaste;
829                 if (cmd.argument().empty() && !theClipboard().isInternal())
830                         topaste = theClipboard().getAsText(frontend::Clipboard::PlainTextType);
831                 else {
832                         size_t n = 0;
833                         idocstringstream is(cmd.argument());
834                         is >> n;
835                         topaste = cap::selection(n, make_pair(buffer().params().documentClassPtr(),
836                                                               buffer().params().authors()));
837                 }
838                 InsetMath const * im = cur.inset().asInsetMath();
839                 InsetMathMacro const * macro = im ? im->asMacro() : nullptr;
840                 // do not allow pasting a backslash in the name of a macro
841                 if (macro
842                     && macro->displayMode() == InsetMathMacro::DISPLAY_UNFOLDED
843                     && support::contains(topaste, char_type('\\'))) {
844                         LYXERR0("Removing backslash from pasted string");
845                         topaste = subst(topaste, from_ascii("\\"), docstring());
846                 }
847                 cur.niceInsert(topaste, parseflg, false);
848                 cur.clearSelection(); // bug 393
849                 cur.forceBufferUpdate();
850                 cur.finishUndo();
851                 break;
852         }
853
854         case LFUN_CUT:
855                 cur.recordUndo();
856                 cutSelection(cur, true);
857                 cur.message(_("Cut"));
858                 // Prevent stale position >= size crash
859                 // Probably not necessary anymore, see eraseSelection (gb 2005-10-09)
860                 cur.normalize();
861                 cur.forceBufferUpdate();
862                 break;
863
864         case LFUN_MOUSE_PRESS:
865                 lfunMousePress(cur, cmd);
866                 break;
867
868         case LFUN_MOUSE_MOTION:
869                 lfunMouseMotion(cur, cmd);
870                 break;
871
872         case LFUN_MOUSE_RELEASE:
873                 lfunMouseRelease(cur, cmd);
874                 break;
875
876         case LFUN_FINISHED_LEFT: // in math, left is backwards
877         case LFUN_FINISHED_BACKWARD:
878                 cur.bv().cursor() = cur;
879                 break;
880
881         case LFUN_FINISHED_RIGHT: // in math, right is forward
882         case LFUN_FINISHED_FORWARD:
883                 ++cur.pos();
884                 cur.bv().cursor() = cur;
885                 break;
886
887         case LFUN_WORD_RIGHT:
888         case LFUN_WORD_LEFT:
889         case LFUN_WORD_BACKWARD:
890         case LFUN_WORD_FORWARD:
891         case LFUN_CHAR_RIGHT:
892         case LFUN_CHAR_LEFT:
893         case LFUN_CHAR_BACKWARD:
894         case LFUN_CHAR_FORWARD:
895                 cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
896                 // fall through
897         case LFUN_WORD_RIGHT_SELECT:
898         case LFUN_WORD_LEFT_SELECT:
899         case LFUN_WORD_BACKWARD_SELECT:
900         case LFUN_WORD_FORWARD_SELECT:
901         case LFUN_CHAR_RIGHT_SELECT:
902         case LFUN_CHAR_LEFT_SELECT:
903         case LFUN_CHAR_BACKWARD_SELECT:
904         case LFUN_CHAR_FORWARD_SELECT: {
905                 // are we in a selection?
906                 bool select = (act == LFUN_WORD_RIGHT_SELECT
907                                            || act == LFUN_WORD_LEFT_SELECT
908                                            || act == LFUN_WORD_BACKWARD_SELECT
909                                            || act == LFUN_WORD_FORWARD_SELECT
910                                || act == LFUN_CHAR_RIGHT_SELECT
911                                            || act == LFUN_CHAR_LEFT_SELECT
912                                            || act == LFUN_CHAR_BACKWARD_SELECT
913                                            || act == LFUN_CHAR_FORWARD_SELECT);
914                 // select words
915                 bool word = (act == LFUN_WORD_RIGHT_SELECT
916                              || act == LFUN_WORD_LEFT_SELECT
917                              || act == LFUN_WORD_BACKWARD_SELECT
918                              || act == LFUN_WORD_FORWARD_SELECT
919                              || act == LFUN_WORD_RIGHT
920                              || act == LFUN_WORD_LEFT
921                              || act == LFUN_WORD_BACKWARD
922                              || act == LFUN_WORD_FORWARD);
923                 // are we moving forward or backwards?
924                 // If the command was RIGHT or LEFT, then whether we're moving forward
925                 // or backwards depends on the cursor movement mode (logical or visual):
926                 //  * in visual mode, since math is always LTR, right -> forward,
927                 //    left -> backwards
928                 //  * in logical mode, the mapping is determined by the
929                 //    reverseDirectionNeeded() function
930
931                 bool forward;
932                 FuncCode finish_lfun;
933
934                 if (act == LFUN_CHAR_FORWARD
935                     || act == LFUN_CHAR_FORWARD_SELECT
936                     || act == LFUN_WORD_FORWARD
937                     || act == LFUN_WORD_FORWARD_SELECT) {
938                         forward = true;
939                         finish_lfun = LFUN_FINISHED_FORWARD;
940                 }
941                 else if (act == LFUN_CHAR_BACKWARD
942                          || act == LFUN_CHAR_BACKWARD_SELECT
943                          || act == LFUN_WORD_BACKWARD
944                          || act == LFUN_WORD_BACKWARD_SELECT) {
945                         forward = false;
946                         finish_lfun = LFUN_FINISHED_BACKWARD;
947                 }
948                 else {
949                         bool right = (act == LFUN_CHAR_RIGHT_SELECT
950                                                   || act == LFUN_CHAR_RIGHT
951                                       || act == LFUN_WORD_RIGHT_SELECT
952                                       || act == LFUN_WORD_RIGHT);
953                         if (lyxrc.visual_cursor || !cur.reverseDirectionNeeded())
954                                 forward = right;
955                         else
956                                 forward = !right;
957
958                         if (right)
959                                 finish_lfun = LFUN_FINISHED_RIGHT;
960                         else
961                                 finish_lfun = LFUN_FINISHED_LEFT;
962                 }
963                 // Now that we know exactly what we want to do, let's do it!
964                 cur.selHandle(select);
965                 cur.clearTargetX();
966                 cur.macroModeClose();
967                 // try moving forward or backwards as necessary...
968                 if (!(forward ? cur.mathForward(word) : cur.mathBackward(word))) {
969                         // ... and if movement failed, then finish forward or backwards
970                         // as necessary
971                         cmd = FuncRequest(finish_lfun);
972                         cur.undispatched();
973                 }
974                 break;
975         }
976
977         case LFUN_DOWN:
978         case LFUN_UP:
979         case LFUN_PARAGRAPH_UP:
980         case LFUN_PARAGRAPH_DOWN:
981                 cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
982                 // fall through
983         case LFUN_DOWN_SELECT:
984         case LFUN_UP_SELECT:
985         case LFUN_PARAGRAPH_UP_SELECT:
986         case LFUN_PARAGRAPH_DOWN_SELECT: {
987                 // close active macro
988                 if (cur.inMacroMode()) {
989                         cur.macroModeClose();
990                         break;
991                 }
992
993                 // stop/start the selection
994                 bool select = act == LFUN_DOWN_SELECT
995                         || act == LFUN_UP_SELECT
996                         || act == LFUN_PARAGRAPH_DOWN_SELECT
997                         || act == LFUN_PARAGRAPH_UP_SELECT;
998                 cur.selHandle(select);
999
1000                 // go up/down
1001                 bool up = act == LFUN_UP || act == LFUN_UP_SELECT
1002                         || act == LFUN_PARAGRAPH_UP || act == LFUN_PARAGRAPH_UP_SELECT;
1003                 bool successful = cur.upDownInMath(up);
1004                 if (successful)
1005                         break;
1006
1007                 if (cur.fixIfBroken())
1008                         // FIXME: Something bad happened. We pass the corrected Cursor
1009                         // instead of letting things go worse.
1010                         break;
1011
1012                 // We did not manage to move the cursor.
1013                 cur.undispatched();
1014                 break;
1015         }
1016
1017         case LFUN_MOUSE_DOUBLE:
1018         case LFUN_WORD_SELECT:
1019                 cur.pos() = 0;
1020                 cur.bv().mouseSetCursor(cur);
1021                 cur.pos() = cur.lastpos();
1022                 cur.bv().mouseSetCursor(cur, true);
1023                 break;
1024
1025         case LFUN_MOUSE_TRIPLE:
1026                 cur.idx() = 0;
1027                 cur.pos() = 0;
1028                 cur.bv().mouseSetCursor(cur);
1029                 cur.idx() = cur.lastidx();
1030                 cur.pos() = cur.lastpos();
1031                 cur.bv().mouseSetCursor(cur, true);
1032                 break;
1033
1034         case LFUN_LINE_BEGIN:
1035                 cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
1036                 // fall through
1037         case LFUN_LINE_BEGIN_SELECT:
1038                 cur.selHandle(act == LFUN_WORD_BACKWARD_SELECT ||
1039                                 act == LFUN_WORD_LEFT_SELECT ||
1040                                 act == LFUN_LINE_BEGIN_SELECT);
1041                 cur.macroModeClose();
1042                 if (cur.pos() != 0) {
1043                         cur.pos() = 0;
1044                 } else if (cur.col() != 0) {
1045                         cur.idx() -= cur.col();
1046                         cur.pos() = 0;
1047                 } else if (cur.idx() != 0) {
1048                         cur.idx() = 0;
1049                         cur.pos() = 0;
1050                 } else {
1051                         cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
1052                         cur.undispatched();
1053                 }
1054                 break;
1055
1056         case LFUN_LINE_END:
1057                 cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
1058                 // fall through
1059         case LFUN_LINE_END_SELECT:
1060                 cur.selHandle(act == LFUN_WORD_FORWARD_SELECT ||
1061                                 act == LFUN_WORD_RIGHT_SELECT ||
1062                                 act == LFUN_LINE_END_SELECT);
1063                 cur.macroModeClose();
1064                 cur.clearTargetX();
1065                 if (cur.pos() != cur.lastpos()) {
1066                         cur.pos() = cur.lastpos();
1067                 } else if (ncols() && (cur.col() != cur.lastcol())) {
1068                         cur.idx() = cur.idx() - cur.col() + cur.lastcol();
1069                         cur.pos() = cur.lastpos();
1070                 } else if (cur.idx() != cur.lastidx()) {
1071                         cur.idx() = cur.lastidx();
1072                         cur.pos() = cur.lastpos();
1073                 } else {
1074                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
1075                         cur.undispatched();
1076                 }
1077                 break;
1078
1079         case LFUN_CELL_FORWARD:
1080                 cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
1081                 cur.selHandle(false);
1082                 cur.clearTargetX();
1083                 cur.macroModeClose();
1084                 if (!cur.inset().idxNext(cur)) {
1085                         if (cur.lastidx() == 0)
1086                                 cur.popForward();
1087                         else {
1088                                 cur.idx() = firstIdx();
1089                                 cur.pos() = 0;
1090                         }
1091                 }
1092                 break;
1093
1094         case LFUN_CELL_BACKWARD:
1095                 cur.screenUpdateFlags(Update::Decoration | Update::FitCursor);
1096                 cur.selHandle(false);
1097                 cur.clearTargetX();
1098                 cur.macroModeClose();
1099                 if (!cur.inset().idxPrev(cur)) {
1100                         if (cur.lastidx() == 0)
1101                                 cur.popBackward();
1102                         else {
1103                                 cur.idx() = cur.lastidx();
1104                                 cur.pos() = lyxrc.mac_like_cursor_movement ? cur.lastpos() : 0;
1105                         }
1106                 }
1107                 break;
1108
1109         case LFUN_WORD_DELETE_BACKWARD:
1110         case LFUN_CHAR_DELETE_BACKWARD:
1111                 if (cur.pos() == 0)
1112                         // May affect external cell:
1113                         cur.recordUndoInset();
1114                 else if (!cur.inMacroMode())
1115                         cur.recordUndoSelection();
1116                 // if the inset can not be removed from within, delete it
1117                 if (!cur.backspace(cmd.getArg(0) != "confirm")) {
1118                         FuncRequest newcmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
1119                         cur.innerText()->dispatch(cur, newcmd);
1120                 }
1121                 break;
1122
1123         case LFUN_WORD_DELETE_FORWARD:
1124         case LFUN_CHAR_DELETE_FORWARD:
1125                 if (cur.pos() == cur.lastpos())
1126                         // May affect external cell:
1127                         cur.recordUndoInset();
1128                 else
1129                         cur.recordUndoSelection();
1130                 // if the inset can not be removed from within, delete it
1131                 if (!cur.erase(cmd.getArg(0) != "confirm")) {
1132                         FuncRequest newcmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
1133                         cur.innerText()->dispatch(cur, newcmd);
1134                 }
1135                 break;
1136
1137         case LFUN_ESCAPE:
1138                 if (cur.selection())
1139                         cur.clearSelection();
1140                 else  {
1141                         if (cur.inMacroMode())
1142                                 cur.macroModeClose(true);
1143                         else {
1144                                 cmd = FuncRequest(LFUN_FINISHED_FORWARD);
1145                                 cur.undispatched();
1146                         }
1147                 }
1148                 break;
1149
1150 #if 0
1151 // FIXME: resurrect this later
1152         // 'Locks' the math inset. A 'locked' math inset behaves as a unit
1153         // that is traversed by a single <CursorLeft>/<CursorRight>.
1154         case LFUN_INSET_TOGGLE:
1155                 cur.recordUndo();
1156                 lock(!lock());
1157                 cur.popForward();
1158                 break;
1159 #endif
1160
1161         case LFUN_SELF_INSERT:
1162                 // special case first for big delimiters
1163                 if (cmd.argument().size() != 1 && interpretString(cur, cmd.argument()))
1164                         break;
1165
1166                 for (char_type c : cmd.argument()) {
1167                         // Don't record undo steps if we are in macro mode and thus
1168                         // cmd.argument is the next character of the macro name.
1169                         // Otherwise we'll get an invalid cursor if we undo after
1170                         // the macro was finished and the macro is a known command,
1171                         // e.g. sqrt. Cursor::macroModeClose replaces in this case
1172                         // the InsetMathUnknown with name "frac" by an empty
1173                         // InsetMathFrac -> a pos value > 0 is invalid.
1174                         // A side effect is that an undo before the macro is finished
1175                         // undoes the complete macro, not only the last character.
1176                         // At the time we hit '\' we are not in macro mode, still.
1177                         if (!cur.inMacroMode())
1178                                 cur.recordUndoSelection();
1179
1180                         // special handling of space. If we insert an inset
1181                         // via macro mode, we want to put the cursor inside it
1182                         // if relevant. Think typing "\frac<space>".
1183                         if (c == ' '
1184                             && cur.inMacroMode() && cur.macroName() != "\\"
1185                             && cur.macroModeClose() && cur.pos() > 0)
1186                                 cur.editInsertedInset();
1187                         else if (!interpretChar(cur, c)) {
1188                                 cmd = FuncRequest(LFUN_FINISHED_FORWARD);
1189                                 cur.undispatched();
1190                                 // FIXME: can we avoid skipping the end of the string?
1191                                 break;
1192                         }
1193                 }
1194                 break;
1195
1196         //case LFUN_SERVER_GET_XY:
1197         //      break;
1198
1199         case LFUN_SERVER_SET_XY: {
1200                 lyxerr << "LFUN_SERVER_SET_XY broken!" << endl;
1201                 int x = 0;
1202                 int y = 0;
1203                 istringstream is(to_utf8(cmd.argument()));
1204                 is >> x >> y;
1205                 cur.setTargetX(x);
1206                 break;
1207         }
1208
1209         // Special casing for superscript in case of LyX handling
1210         // dead-keys:
1211         case LFUN_ACCENT_CIRCUMFLEX:
1212                 if (cmd.argument().empty()) {
1213                         // do superscript if LyX handles
1214                         // deadkeys
1215                         cur.recordUndoInset();
1216                         script(cur, true, grabAndEraseSelection(cur));
1217                 }
1218                 break;
1219
1220         case LFUN_ACCENT_UMLAUT:
1221         case LFUN_ACCENT_ACUTE:
1222         case LFUN_ACCENT_GRAVE:
1223         case LFUN_ACCENT_BREVE:
1224         case LFUN_ACCENT_DOT:
1225         case LFUN_ACCENT_MACRON:
1226         case LFUN_ACCENT_CARON:
1227         case LFUN_ACCENT_TILDE:
1228         case LFUN_ACCENT_CEDILLA:
1229         case LFUN_ACCENT_CIRCLE:
1230         case LFUN_ACCENT_UNDERDOT:
1231         case LFUN_ACCENT_TIE:
1232         case LFUN_ACCENT_OGONEK:
1233         case LFUN_ACCENT_HUNGARIAN_UMLAUT:
1234                 break;
1235
1236         //  Math fonts
1237         case LFUN_TEXTSTYLE_APPLY:
1238         case LFUN_TEXTSTYLE_UPDATE:
1239                 handleFont2(cur, cmd.argument());
1240                 break;
1241
1242         case LFUN_FONT_BOLD:
1243                 if (currentMode() != MATH_MODE)
1244                         handleFont(cur, cmd.argument(), "textbf");
1245                 else
1246                         handleFont(cur, cmd.argument(), "mathbf");
1247                 break;
1248         case LFUN_FONT_BOLDSYMBOL:
1249                 if (currentMode() != MATH_MODE)
1250                         handleFont(cur, cmd.argument(), "textbf");
1251                 else
1252                         handleFont(cur, cmd.argument(), "boldsymbol");
1253                 break;
1254         case LFUN_FONT_SANS:
1255                 if (currentMode() != MATH_MODE)
1256                         handleFont(cur, cmd.argument(), "textsf");
1257                 else
1258                         handleFont(cur, cmd.argument(), "mathsf");
1259                 break;
1260         case LFUN_FONT_EMPH:
1261                 if (currentMode() != MATH_MODE)
1262                         handleFont(cur, cmd.argument(), "emph");
1263                 else
1264                         handleFont(cur, cmd.argument(), "mathcal");
1265                 break;
1266         case LFUN_FONT_ROMAN:
1267                 if (currentMode() != MATH_MODE)
1268                         handleFont(cur, cmd.argument(), "textrm");
1269                 else
1270                         handleFont(cur, cmd.argument(), "mathrm");
1271                 break;
1272         case LFUN_FONT_TYPEWRITER:
1273                 if (currentMode() != MATH_MODE)
1274                         handleFont(cur, cmd.argument(), "texttt");
1275                 else
1276                         handleFont(cur, cmd.argument(), "mathtt");
1277                 break;
1278         case LFUN_FONT_FRAK:
1279                 handleFont(cur, cmd.argument(), "mathfrak");
1280                 break;
1281         case LFUN_FONT_ITAL:
1282                 if (currentMode() != MATH_MODE)
1283                         handleFont(cur, cmd.argument(), "textit");
1284                 else
1285                         handleFont(cur, cmd.argument(), "mathit");
1286                 break;
1287         case LFUN_FONT_NOUN:
1288                 if (currentMode() != MATH_MODE)
1289                         // FIXME: should be "noun"
1290                         handleFont(cur, cmd.argument(), "textsc");
1291                 else
1292                         handleFont(cur, cmd.argument(), "mathbb");
1293                 break;
1294         case LFUN_FONT_DEFAULT:
1295                 handleFont(cur, cmd.argument(), "textnormal");
1296                 break;
1297         case LFUN_FONT_UNDERLINE:
1298                 cur.recordUndo();
1299                 cur.handleNest(createInsetMath("underline", cur.buffer()));
1300                 break;
1301
1302         case LFUN_MATH_MODE: {
1303 #if 1
1304                 // ignore math-mode on when already in math mode
1305                 if (currentMode() == Inset::MATH_MODE && cmd.argument() == "on")
1306                         break;
1307                 cur.recordUndoSelection();
1308                 cur.macroModeClose();
1309                 docstring const save_selection = grabAndEraseSelection(cur);
1310                 selClearOrDel(cur);
1311                 if (currentMode() != Inset::MATH_MODE)
1312                         cur.plainInsert(MathAtom(new InsetMathEnsureMath(buffer_)));
1313                 else
1314                         cur.plainInsert(createInsetMath("text", buffer_));
1315                 cur.posBackward();
1316                 cur.pushBackward(*cur.nextInset());
1317                 cur.niceInsert(save_selection);
1318                 cur.forceBufferUpdate();
1319 #else
1320                 if (currentMode() == Inset::TEXT_MODE) {
1321                         cur.recordUndoSelection();
1322                         cur.niceInsert(MathAtom(new InsetMathHull("simple", cur.buffer())));
1323                         cur.message(_("create new math text environment ($...$)"));
1324                 } else {
1325                         handleFont(cur, cmd.argument(), "textrm");
1326                         cur.message(_("entered math text mode (textrm)"));
1327                 }
1328 #endif
1329                 break;
1330         }
1331
1332         case LFUN_REGEXP_MODE: {
1333                 InsetMath * im = cur.inset().asInsetMath();
1334                 if (im) {
1335                         InsetMathHull * i = im->asHullInset();
1336                         if (i && i->getType() == hullRegexp) {
1337                                 cur.message(_("Already in regular expression mode"));
1338                                 break;
1339                         }
1340                 }
1341                 cur.macroModeClose();
1342                 docstring const save_selection = grabAndEraseSelection(cur);
1343                 selClearOrDel(cur);
1344                 cur.plainInsert(MathAtom(new InsetMathHull(buffer_, hullRegexp)));
1345                 cur.posBackward();
1346                 cur.pushBackward(*cur.nextInset());
1347                 cur.niceInsert(save_selection);
1348                 cur.message(_("Regular expression editor mode"));
1349                 break;
1350         }
1351
1352         case LFUN_MATH_FONT_STYLE: {
1353                 FuncRequest fr = FuncRequest(LFUN_MATH_INSERT, '\\' + cmd.argument());
1354                 doDispatch(cur, fr);
1355                 break;
1356         }
1357
1358         case LFUN_MATH_SIZE: {
1359                 FuncRequest fr = FuncRequest(LFUN_MATH_INSERT, cmd.argument());
1360                 doDispatch(cur, fr);
1361                 break;
1362         }
1363
1364         case LFUN_MATH_MATRIX: {
1365                 cur.recordUndo();
1366                 unsigned int m = 1;
1367                 unsigned int n = 1;
1368                 docstring v_align;
1369                 docstring h_align;
1370                 idocstringstream is(cmd.argument());
1371                 is >> m >> n >> v_align >> h_align;
1372                 if (m < 1)
1373                         m = 1;
1374                 if (n < 1)
1375                         n = 1;
1376                 v_align += 'c';
1377                 cur.niceInsert(MathAtom(new InsetMathArray(buffer_,
1378                         from_ascii("array"), m, n, (char)v_align[0], h_align)));
1379                 break;
1380         }
1381
1382         case LFUN_MATH_AMS_MATRIX: {
1383                 cur.recordUndo();
1384                 unsigned int m = 1;
1385                 unsigned int n = 1;
1386                 docstring name = from_ascii("matrix");
1387                 idocstringstream is(cmd.argument());
1388                 is >> m >> n >> name;
1389                 if (m < 1)
1390                         m = 1;
1391                 if (n < 1)
1392                         n = 1;
1393                 // check if we have a valid decoration
1394                 if (name != "pmatrix" && name != "bmatrix"
1395                         && name != "Bmatrix" && name != "vmatrix"
1396                         && name != "Vmatrix" && name != "matrix"
1397                         && name != "smallmatrix")
1398                         name = from_ascii("matrix");
1399
1400                 cur.niceInsert(
1401                         MathAtom(new InsetMathAMSArray(buffer_, name, m, n)));
1402                 break;
1403         }
1404
1405         case LFUN_MATH_DELIM: {
1406                 docstring ls;
1407                 docstring rs = split(cmd.argument(), ls, ' ');
1408                 // Reasonable default values
1409                 if (ls.empty())
1410                         ls = '(';
1411                 if (rs.empty())
1412                         rs = ')';
1413                 cur.recordUndo();
1414                 cur.handleNest(MathAtom(new InsetMathDelim(buffer_, ls, rs)));
1415                 break;
1416         }
1417
1418         case LFUN_MATH_BIGDELIM: {
1419                 docstring const lname  = from_utf8(cmd.getArg(0));
1420                 docstring const ldelim = from_utf8(cmd.getArg(1));
1421                 docstring const rname  = from_utf8(cmd.getArg(2));
1422                 docstring const rdelim = from_utf8(cmd.getArg(3));
1423                 latexkeys const * l = in_word_set(lname);
1424                 bool const have_l = l && l->inset == "big" &&
1425                                     InsetMathBig::isBigInsetDelim(ldelim);
1426                 l = in_word_set(rname);
1427                 bool const have_r = l && l->inset == "big" &&
1428                                     InsetMathBig::isBigInsetDelim(rdelim);
1429                 // We mimic LFUN_MATH_DELIM in case we have an empty left
1430                 // or right delimiter.
1431                 if (have_l || have_r) {
1432                         cur.recordUndo();
1433                         docstring const selection = grabAndEraseSelection(cur);
1434                         selClearOrDel(cur);
1435                         if (have_l)
1436                                 cur.insert(MathAtom(new InsetMathBig(buffer_, lname, ldelim)));
1437                         // first insert the right delimiter and then go back
1438                         // and re-insert the selection (bug 7088)
1439                         if (have_r) {
1440                                 cur.insert(MathAtom(new InsetMathBig(buffer_, rname, rdelim)));
1441                                 cur.posBackward();
1442                         }
1443                         cur.niceInsert(selection);
1444                 }
1445                 // Don't call cur.undispatched() if we did nothing, this would
1446                 // lead to infinite recursion via Text::dispatch().
1447                 break;
1448         }
1449
1450         case LFUN_SPACE_INSERT: {
1451                 cur.recordUndoSelection();
1452                 string const name = cmd.getArg(0);
1453                 if (name == "normal")
1454                         cur.insert(MathAtom(new InsetMathSpace(buffer_, " ", "")));
1455                 else if (name == "protected")
1456                         cur.insert(MathAtom(new InsetMathSpace(buffer_, "~", "")));
1457                 else if (name == "thin" || name == "med" || name == "thick")
1458                         cur.insert(MathAtom(new InsetMathSpace(buffer_, name + "space", "")));
1459                 else if (name == "hfill*")
1460                         cur.insert(MathAtom(new InsetMathSpace(buffer_, "hspace*{\\fill}", "")));
1461                 else if (name == "quad" || name == "qquad" ||
1462                          name == "enspace" || name == "enskip" ||
1463                          name == "negthinspace" || name == "negmedspace" ||
1464                          name == "negthickspace" || name == "hfill")
1465                         cur.insert(MathAtom(new InsetMathSpace(buffer_, name, "")));
1466                 else if (name == "hspace" || name == "hspace*") {
1467                         string const len = cmd.getArg(1);
1468                         if (len.empty() || !isValidLength(len)) {
1469                                 lyxerr << "LyX function 'space-insert " << name << "' "
1470                                           "needs a valid length argument." << endl;
1471                                 break;
1472                         }
1473                         cur.insert(MathAtom(new InsetMathSpace(buffer_, name, len)));
1474                 } else
1475                         cur.insert(MathAtom(new InsetMathSpace(buffer_)));
1476                 break;
1477         }
1478
1479         case LFUN_MATH_SPACE:
1480                 cur.recordUndoSelection();
1481                 if (cmd.argument().empty())
1482                         cur.insert(MathAtom(new InsetMathSpace(buffer_)));
1483                 else {
1484                         string const name = cmd.getArg(0);
1485                         string const len = cmd.getArg(1);
1486                         cur.insert(MathAtom(new InsetMathSpace(buffer_, name, len)));
1487                 }
1488                 break;
1489
1490         case LFUN_ERT_INSERT:
1491                 // interpret this as if a backslash was typed
1492                 cur.recordUndo();
1493                 interpretChar(cur, '\\');
1494                 break;
1495
1496         case LFUN_MATH_SUBSCRIPT:
1497                 // interpret this as if a _ was typed
1498                 cur.recordUndoSelection();
1499                 interpretChar(cur, '_');
1500                 break;
1501
1502         case LFUN_MATH_SUPERSCRIPT:
1503                 // interpret this as if a ^ was typed
1504                 cur.recordUndoSelection();
1505                 interpretChar(cur, '^');
1506                 break;
1507
1508         case LFUN_MATH_MACRO_FOLD:
1509         case LFUN_MATH_MACRO_UNFOLD: {
1510                 Cursor it = cur;
1511                 bool fold = act == LFUN_MATH_MACRO_FOLD;
1512                 bool found = findMacroToFoldUnfold(it, fold);
1513                 if (found) {
1514                         InsetMathMacro * macro = it.nextInset()->asInsetMath()->asMacro();
1515                         cur.recordUndoInset();
1516                         if (fold)
1517                                 macro->fold(cur);
1518                         else
1519                                 macro->unfold(cur);
1520                 }
1521                 break;
1522         }
1523
1524         case LFUN_QUOTE_INSERT:
1525                 // interpret this as if a straight " was typed
1526                 cur.recordUndoSelection();
1527                 interpretChar(cur, '\"');
1528                 break;
1529
1530 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
1531 // handling such that "self-insert" works on "arbitrary stuff" too, and
1532 // math-insert only handles special math things like "matrix".
1533         case LFUN_MATH_INSERT: {
1534                 cur.recordUndoSelection();
1535                 if (cmd.argument() == "^" || cmd.argument() == "_")
1536                         interpretChar(cur, cmd.argument()[0]);
1537                 else if (!cur.selection())
1538                         cur.niceInsert(cmd.argument());
1539                 else {
1540                         MathData ar(cur.buffer());
1541                         asArray(cmd.argument(), ar);
1542                         if (ar.size() == 1
1543                             && ar[0]->asNestInset()
1544                             && ar[0]->asNestInset()->nargs() > 1)
1545                                 handleNest(cur, ar[0]);
1546                         else
1547                                 cur.niceInsert(cmd.argument());
1548                 }
1549                 break;
1550         }
1551
1552         case LFUN_DIALOG_SHOW_NEW_INSET: {
1553                 docstring const & name = cmd.argument();
1554                 string data;
1555                 if (name == "ref") {
1556                         InsetMathRef tmp(buffer_, name);
1557                         data = tmp.createDialogStr();
1558                         cur.bv().showDialog(to_utf8(name), data);
1559                 } else if (name == "mathspace") {
1560                         cur.bv().showDialog(to_utf8(name));
1561                 }
1562                 break;
1563         }
1564
1565         case LFUN_INSET_INSERT: {
1566                 MathData ar(buffer_);
1567                 if (createInsetMath_fromDialogStr(cmd.argument(), ar)) {
1568                         cur.recordUndoSelection();
1569                         cur.insert(ar);
1570                         cur.forceBufferUpdate();
1571                 } else
1572                         cur.undispatched();
1573                 break;
1574         }
1575         case LFUN_INSET_DISSOLVE:
1576                 if (cmd.argument().empty() && !asHullInset() && !asMacroTemplate()) {
1577                         // we have been triggered via the AtPoint mechanism
1578                         if (cur.nextInset() == this)
1579                                 cur.push(*this);
1580                         cur.recordUndoInset();
1581                         cur.pullArg();
1582                 }
1583                 break;
1584
1585         case LFUN_MATH_LIMITS: {
1586                 InsetMath * in = 0;
1587                 if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
1588                         in = &cur.nextMath();
1589                 else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
1590                         in = &cur.prevMath();
1591                 else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
1592                         in = cur.cell().back().nucleus();
1593                 // only when nucleus allows this
1594                 if (!in)
1595                         return;
1596                 cur.recordUndoInset();
1597                 if (!cmd.argument().empty()) {
1598                         if (cmd.argument() == "limits")
1599                                 in->limits(LIMITS);
1600                         else if (cmd.argument() == "nolimits")
1601                                 in->limits(NO_LIMITS);
1602                         else
1603                                 in->limits(AUTO_LIMITS);
1604                 } else if (in->limits() != AUTO_LIMITS)
1605                         in->limits(AUTO_LIMITS);
1606                 else if (in->defaultLimits(cur.cell().displayStyle()) == LIMITS)
1607                         in->limits(NO_LIMITS);
1608                 else
1609                         in->limits(LIMITS);
1610                 return;
1611         }
1612
1613         case LFUN_PHANTOM_INSERT: {
1614                 docstring const & arg = cmd.argument();
1615                 docstring newarg;
1616                 if (arg == "Phantom")
1617                         newarg = from_ascii("\\phantom");
1618                 else if (arg == "HPhantom")
1619                         newarg = from_ascii("\\hphantom");
1620                 else if (arg == "VPhantom")
1621                         newarg = from_ascii("\\vphantom");
1622                 if (newarg.empty())
1623                         LYXERR0("Unknown phantom type " + newarg);
1624                 else {
1625                         FuncRequest const newfunc(LFUN_MATH_INSERT, newarg);
1626                         lyx::dispatch(newfunc);
1627                 }
1628                 break;
1629         }
1630
1631         default:
1632                 InsetMath::doDispatch(cur, cmd);
1633                 break;
1634         }
1635 }
1636
1637
1638 bool InsetMathNest::findMacroToFoldUnfold(Cursor & it, bool fold) const {
1639         // look for macro to open/close, but stay in mathed
1640         for (; !it.empty(); it.pop_back()) {
1641
1642                 // go backward through the current cell
1643                 Inset * inset = it.nextInset();
1644                 while (inset && inset->asInsetMath()) {
1645                         InsetMathMacro * macro = inset->asInsetMath()->asMacro();
1646                         if (macro) {
1647                                 // found the an macro to open/close?
1648                                 if (macro->folded() != fold)
1649                                         return true;
1650
1651                                 // Wrong folding state.
1652                                 // If this was the first we see in this slice, look further left,
1653                                 // otherwise go up.
1654                                 if (inset != it.nextInset())
1655                                         break;
1656                         }
1657
1658                         // go up if this was the left most position
1659                         if (it.pos() == 0)
1660                                 break;
1661
1662                         // go left
1663                         it.pos()--;
1664                         inset = it.nextInset();
1665                 }
1666         }
1667
1668         return false;
1669 }
1670
1671
1672 bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
1673                 FuncStatus & flag) const
1674 {
1675         // the font related toggles
1676         //string tc = "mathnormal";
1677         bool ret = true;
1678         string const arg = to_utf8(cmd.argument());
1679         switch (cmd.action()) {
1680 #if 0
1681         case LFUN_INSET_MODIFY:
1682                 // FIXME: check temporarily disabled
1683                 // valign code
1684                 char align = mathcursor::valign();
1685                 if (align == '\0') {
1686                         enable = false;
1687                         break;
1688                 }
1689                 if (cmd.argument().empty()) {
1690                         flag.clear();
1691                         break;
1692                 }
1693                 if (!contains("tcb", cmd.argument()[0])) {
1694                         enable = false;
1695                         break;
1696                 }
1697                 flag.setOnOff(cmd.argument()[0] == align);
1698                 break;
1699 #endif
1700         /// We have to handle them since 1.4 blocks all unhandled actions
1701         case LFUN_FONT_ITAL:
1702         case LFUN_FONT_BOLD:
1703         case LFUN_FONT_BOLDSYMBOL:
1704         case LFUN_FONT_SANS:
1705         case LFUN_FONT_EMPH:
1706         case LFUN_FONT_TYPEWRITER:
1707         case LFUN_FONT_NOUN:
1708         case LFUN_FONT_ROMAN:
1709         case LFUN_FONT_DEFAULT:
1710                 flag.setEnabled(true);
1711                 break;
1712
1713         // we just need to be in math mode to enable that
1714         case LFUN_MATH_SIZE:
1715         case LFUN_MATH_SPACE:
1716         case LFUN_MATH_EXTERN:
1717                 flag.setEnabled(true);
1718                 break;
1719
1720         case LFUN_FONT_UNDERLINE:
1721         case LFUN_FONT_FRAK:
1722                 flag.setEnabled(currentMode() != TEXT_MODE);
1723                 break;
1724
1725         case LFUN_MATH_FONT_STYLE: {
1726                 bool const textarg =
1727                         arg == "textbf"   || arg == "textsf" ||
1728                         arg == "textrm"   || arg == "textmd" ||
1729                         arg == "textit"   || arg == "textsc" ||
1730                         arg == "textsl"   || arg == "textup" ||
1731                         arg == "texttt"   || arg == "textbb" ||
1732                         arg == "textnormal";
1733                 flag.setEnabled(currentMode() != TEXT_MODE || textarg);
1734                 break;
1735         }
1736
1737         case LFUN_MATH_MODE:
1738                 // forbid "math-mode on" in math mode to prevent irritating
1739                 // behaviour of menu entries (bug 6709)
1740                 flag.setEnabled(currentMode() == TEXT_MODE || arg != "on");
1741                 break;
1742
1743         case LFUN_MATH_INSERT:
1744                 flag.setEnabled(currentMode() != TEXT_MODE);
1745                 break;
1746
1747         case LFUN_MATH_AMS_MATRIX:
1748         case LFUN_MATH_MATRIX:
1749                 flag.setEnabled(currentMode() == MATH_MODE);
1750                 break;
1751
1752         case LFUN_INSET_INSERT: {
1753                 // Don't test createMathInset_fromDialogStr(), since
1754                 // getStatus is not called with a valid reference and the
1755                 // dialog would not be applicable.
1756                 string const name = cmd.getArg(0);
1757                 flag.setEnabled(name == "ref" || name == "mathspace");
1758                 break;
1759         }
1760
1761         case LFUN_DIALOG_SHOW_NEW_INSET: {
1762                 docstring const & name = cmd.argument();
1763                 flag.setEnabled(name == "ref" || name == "mathspace");
1764                 break;
1765         }
1766
1767
1768         case LFUN_MATH_DELIM:
1769         case LFUN_MATH_BIGDELIM:
1770                 // Don't do this with multi-cell selections
1771                 flag.setEnabled(cur.selBegin().idx() == cur.selEnd().idx());
1772                 break;
1773
1774         case LFUN_MATH_MACRO_FOLD:
1775         case LFUN_MATH_MACRO_UNFOLD: {
1776                 Cursor it = cur;
1777                 bool found = findMacroToFoldUnfold(it, cmd.action() == LFUN_MATH_MACRO_FOLD);
1778                 flag.setEnabled(found);
1779                 break;
1780         }
1781
1782         case LFUN_SPECIALCHAR_INSERT:
1783         case LFUN_SCRIPT_INSERT:
1784                 // FIXME: These would probably make sense in math-text mode
1785                 flag.setEnabled(false);
1786                 break;
1787
1788         // these are nonfunctional in math
1789         case LFUN_BOX_INSERT:
1790         case LFUN_BRANCH_INSERT:
1791         case LFUN_BRANCH_ADD_INSERT:
1792         case LFUN_CAPTION_INSERT:
1793         case LFUN_FLEX_INSERT:
1794         case LFUN_FLOAT_INSERT:
1795         case LFUN_FLOAT_LIST_INSERT:
1796         case LFUN_FOOTNOTE_INSERT:
1797         case LFUN_HREF_INSERT:
1798         case LFUN_INDEX_INSERT:
1799         case LFUN_INDEX_PRINT:
1800         case LFUN_INFO_INSERT:
1801         case LFUN_IPA_INSERT:
1802         case LFUN_LISTING_INSERT:
1803         case LFUN_MARGINALNOTE_INSERT:
1804         case LFUN_NEWPAGE_INSERT:
1805         case LFUN_NOMENCL_INSERT:
1806         case LFUN_NOMENCL_PRINT:
1807         case LFUN_NOTE_INSERT:
1808         case LFUN_PREVIEW_INSERT:
1809         case LFUN_TABULAR_INSERT:
1810         case LFUN_WRAP_INSERT:
1811                 flag.setEnabled(false);
1812                 break;
1813
1814         case LFUN_SPACE_INSERT: {
1815                 docstring const & name = cmd.argument();
1816                 if (name == "visible")
1817                         flag.setEnabled(false);
1818                 break;
1819         }
1820
1821         case LFUN_INSET_DISSOLVE:
1822                 flag.setEnabled(cmd.argument().empty() && !asHullInset() && !asMacroTemplate());
1823                 break;
1824
1825         case LFUN_PASTE: {
1826                 docstring const & name = cmd.argument();
1827                 if (name == "html" || name == "latex")
1828                         flag.setEnabled(false);
1829                 break;
1830         }
1831
1832         case LFUN_MATH_LIMITS: {
1833                 InsetMath * in = 0;
1834                 if (cur.pos() < cur.lastpos() && cur.nextMath().allowsLimitsChange())
1835                         in = &cur.nextMath();
1836                 else if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange())
1837                         in = &cur.prevMath();
1838                 else if (cur.lastpos() > 0 && cur.cell().back()->allowsLimitsChange())
1839                         in = cur.cell().back().nucleus();
1840                 if (in) {
1841                         if (!cmd.argument().empty()) {
1842                                 if (cmd.argument() == "limits")
1843                                         flag.setOnOff(in->limits() == LIMITS);
1844                                 else if (cmd.argument() == "nolimits")
1845                                         flag.setOnOff(in->limits() == NO_LIMITS);
1846                                 else
1847                                         flag.setOnOff(in->limits() == AUTO_LIMITS);
1848                         }
1849                         flag.setEnabled(true);
1850                 } else
1851                         flag.setEnabled(false);
1852                 return true;
1853         }
1854
1855         default:
1856                 ret = false;
1857                 break;
1858         }
1859         return ret;
1860 }
1861
1862
1863 void InsetMathNest::edit(Cursor & cur, bool front, EntryDirection entry_from)
1864 {
1865         cur.push(*this);
1866         bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_LEFT ||
1867                 (entry_from == Inset::ENTRY_DIRECTION_IGNORE && front));
1868         enter_front ? idxFirst(cur) : idxLast(cur);
1869         cur.resetAnchor();
1870         //lyxerr << "InsetMathNest::edit, cur:\n" << cur << endl;
1871 }
1872
1873
1874 Inset * InsetMathNest::editXY(Cursor & cur, int x, int y)
1875 {
1876         int idx_min = -1;
1877         int dist_min = 1000000;
1878         for (idx_type i = 0, n = nargs(); i != n; ++i) {
1879                 int const d = cell(i).dist(cur.bv(), x, y);
1880                 if (d < dist_min) {
1881                         dist_min = d;
1882                         idx_min = i;
1883                 }
1884         }
1885         if (idx_min == -1)
1886                 return this;
1887
1888         MathData & ar = cell(idx_min);
1889         cur.push(*this);
1890         cur.idx() = idx_min;
1891         cur.pos() = ar.x2pos(&cur.bv(), x - ar.xo(cur.bv()));
1892
1893         //lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
1894         if (dist_min == 0) {
1895                 // hit inside cell
1896                 for (pos_type i = 0, n = ar.size(); i < n; ++i)
1897                         if (ar[i]->covers(cur.bv(), x, y))
1898                                 return ar[i].nucleus()->editXY(cur, x, y);
1899         }
1900         return this;
1901 }
1902
1903
1904 void InsetMathNest::lfunMousePress(Cursor & cur, FuncRequest & cmd)
1905 {
1906         //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
1907         BufferView & bv = cur.bv();
1908         if (cmd.button() == mouse_button::button3) {
1909                 // Don't do anything if we right-click a
1910                 // selection, a context menu will popup.
1911                 if (bv.cursor().selection() && cur >= bv.cursor().selectionBegin()
1912                       && cur < bv.cursor().selectionEnd()) {
1913                         cur.noScreenUpdate();
1914                         return;
1915                 }
1916         }
1917
1918         // set cursor after the inset if x is nearer to that position (bug 9748)
1919         cur.moveToClosestEdge(cmd.x(), true);
1920
1921         bool do_selection = cmd.button() == mouse_button::button1
1922                 && cmd.modifier() == ShiftModifier;
1923         bv.mouseSetCursor(cur, do_selection);
1924         if (cmd.button() == mouse_button::button1) {
1925                 //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
1926                 // Update the cursor update flags as needed:
1927                 //
1928                 // Update::Decoration: tells to update the decoration
1929                 //                     (visual box corners that define
1930                 //                     the inset)/
1931                 // Update::FitCursor: adjust the screen to the cursor
1932                 //                    position if needed
1933                 // cur.result().update(): don't overwrite previously set flags.
1934                 cur.screenUpdateFlags(Update::Decoration | Update::FitCursor
1935                                 | cur.result().screenUpdate());
1936         } else if (cmd.button() == mouse_button::button2 && lyxrc.mouse_middlebutton_paste) {
1937                 if (cap::selection()) {
1938                         // See comment in Text::dispatch why we do this
1939                         cap::copySelectionToStack();
1940                         cmd = FuncRequest(LFUN_PASTE, "0");
1941                         doDispatch(bv.cursor(), cmd);
1942                 } else {
1943                         MathData ar(buffer_);
1944                         asArray(theSelection().get(), ar);
1945                         bv.cursor().insert(ar);
1946                 }
1947         }
1948 }
1949
1950
1951 void InsetMathNest::lfunMouseMotion(Cursor & cur, FuncRequest & cmd)
1952 {
1953         // only select with button 1
1954         if (cmd.button() != mouse_button::button1)
1955                 return;
1956
1957         Cursor & bvcur = cur.bv().cursor();
1958
1959         // ignore motions deeper nested than the real anchor
1960         if (!bvcur.realAnchor().hasPart(cur)) {
1961                 cur.undispatched();
1962                 return;
1963         }
1964
1965         // set cursor after the inset if x is nearer to that position (bug 9748)
1966         cur.moveToClosestEdge(cmd.x());
1967
1968         CursorSlice old = bvcur.top();
1969
1970         // We continue with our existing selection or start a new one, so don't
1971         // reset the anchor.
1972         bvcur.setCursor(cur);
1973         // Did we actually move?
1974         if (cur.top() == old)
1975                 // We didn't move one iota, so no need to change selection status
1976                 // or update the screen.
1977                 cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor);
1978         else
1979                 bvcur.setSelection();
1980 }
1981
1982
1983 void InsetMathNest::lfunMouseRelease(Cursor & cur, FuncRequest & cmd)
1984 {
1985         //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
1986
1987         if (cmd.button() == mouse_button::button1) {
1988                 if (!cur.selection())
1989                         cur.noScreenUpdate();
1990                 else {
1991                         Cursor & bvcur = cur.bv().cursor();
1992                         bvcur.selection(true);
1993                 }
1994                 return;
1995         }
1996
1997         cur.undispatched();
1998 }
1999
2000
2001 bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
2002 {
2003         // try auto-correction
2004         if (lyxrc.autocorrection_math && cur.pos() != 0 && !cur.selection()
2005              && math_autocorrect(cur, c))
2006                 return true;
2007
2008         //lyxerr << "interpret 2: '" << c << "'" << endl;
2009         docstring save_selection;
2010         if (c == '^' || c == '_')
2011                 save_selection = grabAndEraseSelection(cur);
2012
2013         cur.clearTargetX();
2014         Buffer * buf = cur.buffer();
2015
2016         // handle macroMode
2017         if (cur.inMacroMode()) {
2018                 docstring name = cur.macroName();
2019
2020                 /// are we currently typing '#1' or '#2' or...?
2021                 if (name == "\\#") {
2022                         cur.backspace();
2023                         int n = c - '0';
2024                         if (n >= 1 && n <= 9)
2025                                 cur.insert(new InsetMathMacroArgument(buffer_, n));
2026                         return true;
2027                 }
2028
2029                 // do not finish macro for known * commands
2030                 bool star_macro = c == '*'
2031                         && (in_word_set(name.substr(1) + '*')
2032                             || cur.buffer()->getMacro(name.substr(1) + "*", cur, true));
2033                 if (isAlphaASCII(c) || star_macro) {
2034                         cur.activeMacro()->setName(name + docstring(1, c));
2035                         return true;
2036                 }
2037
2038                 // handle 'special char' macros
2039                 if (name == "\\") {
2040                         // remove the '\\'
2041                         if (c == '\\') {
2042                                 cur.backspace();
2043                                 if (currentMode() != InsetMath::MATH_MODE)
2044                                         cur.niceInsert(createInsetMath("textbackslash", buf));
2045                                 else
2046                                         cur.niceInsert(createInsetMath("backslash", buf));
2047                         } else if (c == '^' && currentMode() == InsetMath::MATH_MODE) {
2048                                 cur.backspace();
2049                                 cur.niceInsert(createInsetMath("mathcircumflex", buf));
2050                         } else if (c == '{' || c == '%') {
2051                                 //using the saved selection as argument
2052                                 InsetMathUnknown * p = cur.activeMacro();
2053                                 p->finalize();
2054                                 MathData sel(cur.buffer());
2055                                 asArray(p->selection(), sel);
2056                                 cur.backspace();
2057                                 if (c == '{')
2058                                         cur.niceInsert(MathAtom(new InsetMathBrace(buffer_, sel)));
2059                                 else
2060                                         cur.niceInsert(MathAtom(new InsetMathComment(sel)));
2061                         } else if (c == '#') {
2062                                 LASSERT(cur.activeMacro(), return false);
2063                                 cur.activeMacro()->setName(name + docstring(1, c));
2064                         } else {
2065                                 cur.backspace();
2066                                 cur.niceInsert(createInsetMath(docstring(1, c), buf));
2067                         }
2068                         return true;
2069                 }
2070
2071                 // One character big delimiters. The others are handled in
2072                 // interpretString().
2073                 latexkeys const * l = in_word_set(name.substr(1));
2074                 if (name[0] == '\\' && l && l->inset == "big") {
2075                         docstring delim;
2076                         switch (c) {
2077                         case '{':
2078                                 delim = from_ascii("\\{");
2079                                 break;
2080                         case '}':
2081                                 delim = from_ascii("\\}");
2082                                 break;
2083                         default:
2084                                 delim = docstring(1, c);
2085                                 break;
2086                         }
2087                         if (InsetMathBig::isBigInsetDelim(delim)) {
2088                                 // name + delim ared a valid InsetMathBig.
2089                                 // We can't use cur.macroModeClose() because
2090                                 // it does not handle delim.
2091                                 InsetMathUnknown * p = cur.activeMacro();
2092                                 p->finalize();
2093                                 --cur.pos();
2094                                 cur.cell().erase(cur.pos());
2095                                 cur.plainInsert(MathAtom(
2096                                         new InsetMathBig(buffer_, name.substr(1), delim)));
2097                                 return true;
2098                         }
2099                 } else if (name == "\\smash" && c == '[') {
2100                         // We can't use cur.macroModeClose() because
2101                         // it would create an InsetMathPhantom
2102                         InsetMathUnknown * p = cur.activeMacro();
2103                         p->finalize();
2104                         interpretChar(cur, c);
2105                         return true;
2106                 }
2107
2108                 // leave macro mode and try again if necessary
2109                 if (cur.macroModeClose()) {
2110                         MathAtom const atom = cur.prevAtom();
2111                         if (atom->asNestInset() && atom->isActive()) {
2112                                 cur.posBackward();
2113                                 cur.nextInset()->edit(cur, true);
2114                         }
2115                 }
2116                 if (c == '{')
2117                         cur.niceInsert(MathAtom(new InsetMathBrace(buf)));
2118                 else if (c != ' ')
2119                         interpretChar(cur, c);
2120                 return true;
2121         }
2122
2123
2124         // just clear selection on pressing the space bar
2125         if (cur.selection() && c == ' ') {
2126                 cur.selection(false);
2127                 return true;
2128         }
2129
2130         if (c == '\\') {
2131                 //lyxerr << "starting with macro" << endl;
2132                 bool reduced = cap::reduceSelectionToOneCell(cur);
2133                 if (reduced || !cur.selection()) {
2134                         InsetMath const * im = cur.inset().asInsetMath();
2135                         InsetMathMacro const * macro = im ? im->asMacro()
2136                                                           : nullptr;
2137                         bool in_macro_name = macro
2138                                 && macro->displayMode() ==
2139                                         InsetMathMacro::DISPLAY_UNFOLDED;
2140                         cur.recordUndoInset();
2141                         docstring const safe = cap::grabAndEraseSelection(cur);
2142                         if (!cur.inRegexped() && !in_macro_name)
2143                                 cur.insert(MathAtom(new InsetMathUnknown(buffer_, from_ascii("\\"), safe, false)));
2144                         else
2145                                 cur.niceInsert(createInsetMath("backslash", buf));
2146                 }
2147                 return true;
2148         }
2149
2150         selClearOrDel(cur);
2151
2152         if (c == '\n') {
2153                 if (currentMode() != InsetMath::MATH_MODE)
2154                         cur.insert(c);
2155                 return true;
2156         }
2157
2158         if (c == ' ') {
2159                 if (currentMode() != InsetMath::MATH_MODE) {
2160                         // insert spaces in text or undecided mode,
2161                         // but suppress direct insertion of two spaces in a row
2162                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
2163                         // it is better than nothing...
2164                         pos_type const pos = cur.pos();
2165                         pos_type const lastpos = cur.lastpos();
2166                         if ((pos == 0 && lastpos == 0)
2167                             || (pos == 0 && cur.nextAtom()->getChar() != ' ')
2168                             || (pos == lastpos && cur.prevAtom()->getChar() != ' ')
2169                             || (pos > 0 && cur.prevAtom()->getChar() != ' '
2170                                         && cur.nextAtom()->getChar() != ' ')) {
2171                                 cur.insert(c);
2172                                 // FIXME: we have to enable full redraw here because of the
2173                                 // visual box corners that define the inset. If we know for
2174                                 // sure that we stay within the same cell we can optimize for
2175                                 // that using:
2176                                 //cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor);
2177                         }
2178                         return true;
2179                 }
2180                 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
2181                         cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
2182                         // FIXME: we have to enable full redraw here because of the
2183                         // visual box corners that define the inset. If we know for
2184                         // sure that we stay within the same cell we can optimize for
2185                         // that using:
2186                         //cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor);
2187                         return true;
2188                 }
2189
2190                 if (cur.popForward()) {
2191                         // FIXME: we have to enable full redraw here because of the
2192                         // visual box corners that define the inset. If we know for
2193                         // sure that we stay within the same cell we can optimize for
2194                         // that using:
2195                         //cur.screenUpdateFlags(Update::FitCursor);
2196                         return true;
2197                 }
2198
2199                 // if we are at the very end, leave the formula
2200                 return cur.pos() != cur.lastpos();
2201         }
2202
2203         // These should be treated differently when not in text mode:
2204         if (cur.inRegexped()) {
2205                 switch (c) {
2206                 case '^':
2207                         cur.niceInsert(createInsetMath("mathcircumflex", buf));
2208                         break;
2209                 case '{':
2210                 case '}':
2211                 case '#':
2212                 case '%':
2213                 case '_':
2214                         cur.niceInsert(createInsetMath(docstring(1, c), buf));
2215                         break;
2216                 case '~':
2217                         cur.niceInsert(createInsetMath("sim", buf));
2218                         break;
2219                 default:
2220                         cur.insert(c);
2221                 }
2222                 return true;
2223         } else if (currentMode() != InsetMath::TEXT_MODE) {
2224                 if (c == '_') {
2225                         cur.recordUndoInset();
2226                         script(cur, false, save_selection);
2227                         return true;
2228                 }
2229                 if (c == '^') {
2230                         cur.recordUndoInset();
2231                         script(cur, true, save_selection);
2232                         return true;
2233                 }
2234                 if (c == '~') {
2235                         cur.niceInsert(createInsetMath("sim", buf));
2236                         return true;
2237                 }
2238                 if (currentMode() == InsetMath::MATH_MODE && Encodings::isUnicodeTextOnly(c)) {
2239                         MathAtom const atom(new InsetMathChar(buffer_, c));
2240                         if (cur.prevInset() && cur.prevInset()->asInsetMath()->name() == "text") {
2241                                 // reuse existing \text inset
2242                                 cur.prevInset()->asInsetMath()->cell(0).push_back(atom);
2243                         } else {
2244                                 MathAtom at = createInsetMath("text", buf);
2245                                 at.nucleus()->cell(0).push_back(atom);
2246                                 cur.insert(at);
2247                                 cur.posForward();
2248                         }
2249                         return true;
2250                 }
2251         } else {
2252                 if (c == '^') {
2253                         cur.niceInsert(createInsetMath("textasciicircum", buf));
2254                         return true;
2255                 }
2256                 if (c == '~') {
2257                         cur.niceInsert(createInsetMath("textasciitilde", buf));
2258                         return true;
2259                 }
2260         }
2261
2262         if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' ||
2263             c == '%' || c == '_') {
2264                 cur.niceInsert(createInsetMath(docstring(1, c), buf));
2265                 return true;
2266         }
2267
2268         // no special circumstances, so insert the character without any fuss
2269         cur.insert(c);
2270         return true;
2271 }
2272
2273
2274 bool InsetMathNest::interpretString(Cursor & cur, docstring const & str)
2275 {
2276         if (str == "\\limits" || str == "\\nolimits") {
2277                 if (cur.pos() > 0 && cur.prevMath().allowsLimitsChange()) {
2278                         cur.prevMath().limits(str == "\\limits" ? LIMITS : NO_LIMITS);
2279                         return true;
2280                 } else {
2281                         cur.message(bformat(_("Cannot apply %1$s here."), str));
2282                         return false;
2283                 }
2284         }
2285         // Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
2286         // possible
2287         if (!cur.empty() && cur.pos() > 0 &&
2288             cur.cell()[cur.pos() - 1]->asUnknownInset()) {
2289                 if (InsetMathBig::isBigInsetDelim(str)) {
2290                         docstring prev = asString(cur.cell()[cur.pos() - 1]);
2291                         if (prev[0] == '\\') {
2292                                 prev = prev.substr(1);
2293                                 latexkeys const * l = in_word_set(prev);
2294                                 if (l && l->inset == "big") {
2295                                         cur.recordUndoSelection();
2296                                         cur.cell()[cur.pos() - 1] =
2297                                                 MathAtom(new InsetMathBig(buffer_, prev, str));
2298                                         return true;
2299                                 }
2300                         }
2301                 }
2302         }
2303         return false;
2304 }
2305
2306
2307 bool InsetMathNest::script(Cursor & cur, bool up)
2308 {
2309         return script(cur, up, docstring());
2310 }
2311
2312
2313 bool InsetMathNest::script(Cursor & cur, bool up,
2314                 docstring const & save_selection)
2315 {
2316         // Hack to get \^ and \_ working
2317         //lyxerr << "handling script: up: " << up << endl;
2318         if (cur.inMacroMode() && cur.macroName() == "\\") {
2319                 if (up)
2320                         cur.niceInsert(createInsetMath("mathcircumflex", cur.buffer()));
2321                 else
2322                         interpretChar(cur, '_');
2323                 return true;
2324         }
2325
2326         cur.macroModeClose();
2327         if (asScriptInset() && cur.idx() == 0) {
2328                 // we are in a nucleus of a script inset, move to _our_ script
2329                 InsetMathScript * inset = asScriptInset();
2330                 //lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
2331                 inset->ensure(up);
2332                 cur.idx() = inset->idxOfScript(up);
2333                 cur.pos() = 0;
2334         } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
2335                 --cur.pos();
2336                 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
2337                 cur.push(*inset);
2338                 inset->ensure(up);
2339                 cur.idx() = inset->idxOfScript(up);
2340                 cur.pos() = cur.lastpos();
2341         } else {
2342                 // convert the thing to our left to a scriptinset or create a new
2343                 // one if in the very first position of the array
2344                 if (cur.pos() == 0) {
2345                         //lyxerr << "new scriptinset" << endl;
2346                         cur.insert(new InsetMathScript(buffer_, up));
2347                 } else {
2348                         //lyxerr << "converting prev atom " << endl;
2349                         cur.prevAtom() = MathAtom(new InsetMathScript(buffer_, cur.prevAtom(), up));
2350                 }
2351                 --cur.pos();
2352                 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
2353                 // See comment in MathParser.cpp for special handling of {}-bases
2354
2355                 cur.push(*inset);
2356                 cur.idx() = 1;
2357                 cur.pos() = 0;
2358         }
2359         //lyxerr << "inserting selection 1:\n" << save_selection << endl;
2360         cur.niceInsert(save_selection);
2361         cur.resetAnchor();
2362         //lyxerr << "inserting selection 2:\n" << save_selection << endl;
2363         return true;
2364 }
2365
2366
2367 bool InsetMathNest::completionSupported(Cursor const & cur) const
2368 {
2369         return cur.inMacroMode();
2370 }
2371
2372
2373 bool InsetMathNest::inlineCompletionSupported(Cursor const & cur) const
2374 {
2375         return cur.inMacroMode();
2376 }
2377
2378
2379 bool InsetMathNest::automaticInlineCompletion() const
2380 {
2381         return lyxrc.completion_inline_math;
2382 }
2383
2384
2385 bool InsetMathNest::automaticPopupCompletion() const
2386 {
2387         return lyxrc.completion_popup_math;
2388 }
2389
2390
2391 CompletionList const *
2392 InsetMathNest::createCompletionList(Cursor const & cur) const
2393 {
2394         if (!cur.inMacroMode())
2395                 return nullptr;
2396
2397         return new MathCompletionList(cur);
2398 }
2399
2400
2401 docstring InsetMathNest::completionPrefix(Cursor const & cur) const
2402 {
2403         if (!cur.inMacroMode())
2404                 return docstring();
2405
2406         return cur.activeMacro()->name();
2407 }
2408
2409
2410 bool InsetMathNest::insertCompletion(Cursor & cur, docstring const & s, bool finished)
2411 {
2412         if (cur.buffer()->isReadonly() || !cur.inMacroMode())
2413                 return false;
2414
2415         // Contrary to Text, the whole inset should be recorded (#12581).
2416         cur.recordUndoInset();
2417
2418         // append completion to active macro
2419         InsetMathUnknown * inset = cur.activeMacro();
2420         inset->setName(inset->name() + s);
2421
2422         // finish macro
2423         if (finished) {
2424 #if 0
2425                 // FIXME: this creates duplicates in the completion popup
2426                 // which looks ugly. Moreover the changes the list lengths
2427                 // which seems to confuse the popup as well.
2428                 MathCompletionList::addToFavorites(inset->name());
2429 #endif
2430                 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, " "));
2431         }
2432         cur.screenUpdateFlags(Update::SinglePar | Update::FitCursor);
2433
2434         return true;
2435 }
2436
2437
2438 void InsetMathNest::completionPosAndDim(Cursor const & cur, int & x, int & y,
2439                                         Dimension & dim) const
2440 {
2441         Inset const * inset = cur.activeMacro();
2442         if (!inset)
2443                 return;
2444
2445         // get inset dimensions
2446         dim = cur.bv().coordCache().insets().dim(inset);
2447         // FIXME: these 3 are no accurate, but should depend on the font.
2448         // Now the popup jumps down if you enter a char with descent > 0.
2449         dim.des += 3;
2450         dim.asc += 3;
2451
2452         // and position
2453         Point xy = cur.bv().coordCache().insets().xy(inset);
2454         x = xy.x_;
2455         y = xy.y_;
2456 }
2457
2458
2459 ////////////////////////////////////////////////////////////////////
2460
2461 MathCompletionList::MathCompletionList(Cursor const & cur)
2462 {
2463         // fill it with macros from the buffer
2464         MacroNameSet macros;
2465         cur.buffer()->listMacroNames(macros);
2466         MacroNameSet::const_iterator it;
2467         for (it = macros.begin(); it != macros.end(); ++it) {
2468                 if (cur.buffer()->getMacro(*it, cur, false))
2469                         locals.push_back("\\" + *it);
2470         }
2471         sort(locals.begin(), locals.end());
2472
2473         if (!globals.empty())
2474                 return;
2475
2476         // fill in global macros
2477         macros.clear();
2478         MacroTable::globalMacros().getMacroNames(macros, false);
2479         //lyxerr << "Globals completion macros: ";
2480         for (it = macros.begin(); it != macros.end(); ++it) {
2481                 //lyxerr << "\\" + *it << " ";
2482                 globals.push_back("\\" + *it);
2483         }
2484         //lyxerr << std::endl;
2485
2486         // fill in global commands
2487         globals.push_back(from_ascii("\\boxed"));
2488         globals.push_back(from_ascii("\\fbox"));
2489         globals.push_back(from_ascii("\\framebox"));
2490         globals.push_back(from_ascii("\\makebox"));
2491         globals.push_back(from_ascii("\\kern"));
2492         globals.push_back(from_ascii("\\xhookrightarrow"));
2493         globals.push_back(from_ascii("\\xhookleftarrow"));
2494         globals.push_back(from_ascii("\\xrightarrow"));
2495         globals.push_back(from_ascii("\\xRightarrow"));
2496         globals.push_back(from_ascii("\\xrightharpoondown"));
2497         globals.push_back(from_ascii("\\xrightharpoonup"));
2498         globals.push_back(from_ascii("\\xrightleftharpoons"));
2499         globals.push_back(from_ascii("\\xleftarrow"));
2500         globals.push_back(from_ascii("\\xLeftarrow"));
2501         globals.push_back(from_ascii("\\xleftharpoondown"));
2502         globals.push_back(from_ascii("\\xleftharpoonup"));
2503         globals.push_back(from_ascii("\\xleftrightarrow"));
2504         globals.push_back(from_ascii("\\xLeftrightarrow"));
2505         globals.push_back(from_ascii("\\xleftrightharpoons"));
2506         globals.push_back(from_ascii("\\xmapsto"));
2507         globals.push_back(from_ascii("\\split"));
2508         globals.push_back(from_ascii("\\gathered"));
2509         globals.push_back(from_ascii("\\aligned"));
2510         globals.push_back(from_ascii("\\alignedat"));
2511         globals.push_back(from_ascii("\\cases"));
2512         globals.push_back(from_ascii("\\substack"));
2513         globals.push_back(from_ascii("\\xymatrix"));
2514         globals.push_back(from_ascii("\\Diagram"));
2515         globals.push_back(from_ascii("\\subarray"));
2516         globals.push_back(from_ascii("\\array"));
2517         globals.push_back(from_ascii("\\sqrt"));
2518         globals.push_back(from_ascii("\\root"));
2519         globals.push_back(from_ascii("\\tabular"));
2520         globals.push_back(from_ascii("\\sideset"));
2521         globals.push_back(from_ascii("\\stackrel"));
2522         globals.push_back(from_ascii("\\stackrelthree"));
2523         globals.push_back(from_ascii("\\binom"));
2524         globals.push_back(from_ascii("\\choose"));
2525         globals.push_back(from_ascii("\\brace"));
2526         globals.push_back(from_ascii("\\brack"));
2527         globals.push_back(from_ascii("\\frac"));
2528         globals.push_back(from_ascii("\\over"));
2529         globals.push_back(from_ascii("\\nicefrac"));
2530         globals.push_back(from_ascii("\\unitfrac"));
2531         globals.push_back(from_ascii("\\unitfracthree"));
2532         globals.push_back(from_ascii("\\unitone"));
2533         globals.push_back(from_ascii("\\unittwo"));
2534         globals.push_back(from_ascii("\\infer"));
2535         globals.push_back(from_ascii("\\atop"));
2536         globals.push_back(from_ascii("\\lefteqn"));
2537         globals.push_back(from_ascii("\\boldsymbol"));
2538         globals.push_back(from_ascii("\\bm"));
2539         globals.push_back(from_ascii("\\color"));
2540         globals.push_back(from_ascii("\\normalcolor"));
2541         globals.push_back(from_ascii("\\textcolor"));
2542         globals.push_back(from_ascii("\\cfrac"));
2543         globals.push_back(from_ascii("\\cfracleft"));
2544         globals.push_back(from_ascii("\\cfracright"));
2545         globals.push_back(from_ascii("\\dfrac"));
2546         globals.push_back(from_ascii("\\tfrac"));
2547         globals.push_back(from_ascii("\\dbinom"));
2548         globals.push_back(from_ascii("\\tbinom"));
2549         globals.push_back(from_ascii("\\hphantom"));
2550         globals.push_back(from_ascii("\\phantom"));
2551         globals.push_back(from_ascii("\\vphantom"));
2552         globals.push_back(from_ascii("\\cancel"));
2553         globals.push_back(from_ascii("\\bcancel"));
2554         globals.push_back(from_ascii("\\xcancel"));
2555         globals.push_back(from_ascii("\\cancelto"));
2556         globals.push_back(from_ascii("\\smash"));
2557         globals.push_back(from_ascii("\\mathclap"));
2558         globals.push_back(from_ascii("\\mathllap"));
2559         globals.push_back(from_ascii("\\mathrlap"));
2560         globals.push_back(from_ascii("\\ensuremath"));
2561         MathWordList const & words = mathedWordList();
2562         MathWordList::const_iterator it2;
2563         //lyxerr << "Globals completion commands: ";
2564         for (it2 = words.begin(); it2 != words.end(); ++it2) {
2565                 if (it2->second.inset != "macro" && !it2->second.hidden) {
2566                         // macros are already read from MacroTable::globalMacros()
2567                         globals.push_back('\\' + it2->first);
2568                         //lyxerr << '\\' + it2->first << ' ';
2569                 }
2570         }
2571         //lyxerr << std::endl;
2572         sort(globals.begin(), globals.end());
2573 }
2574
2575
2576 MathCompletionList::~MathCompletionList()
2577 {
2578 }
2579
2580
2581 size_type MathCompletionList::size() const
2582 {
2583         return locals.size() + globals.size();
2584 }
2585
2586
2587 docstring const & MathCompletionList::data(size_t idx) const
2588 {
2589         size_t lsize = locals.size();
2590         if (idx >= lsize)
2591                 return globals[idx - lsize];
2592         else
2593                 return locals[idx];
2594 }
2595
2596
2597 std::string MathCompletionList::icon(size_t idx) const
2598 {
2599         // get the latex command
2600         docstring cmd;
2601         size_t lsize = locals.size();
2602         if (idx >= lsize)
2603                 cmd = globals[idx - lsize];
2604         else
2605                 cmd = locals[idx];
2606
2607         // get the icon name by stripping the backslash
2608         docstring icon_name = frontend::Application::mathIcon(cmd.substr(1));
2609         if (icon_name.empty())
2610                 return std::string();
2611         return "math/" + to_utf8(icon_name);
2612 }
2613
2614 std::vector<docstring> MathCompletionList::globals;
2615
2616 } // namespace lyx