]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathNest.cpp
Fix crash when selecting part of a macro and trying to resume macro-mode editing.
[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 "InsetMathBig.h"
17 #include "InsetMathBox.h"
18 #include "InsetMathBrace.h"
19 #include "InsetMathChar.h"
20 #include "InsetMathColor.h"
21 #include "InsetMathComment.h"
22 #include "InsetMathDelim.h"
23 #include "InsetMathEnsureMath.h"
24 #include "InsetMathHull.h"
25 #include "InsetMathRef.h"
26 #include "InsetMathScript.h"
27 #include "InsetMathSpace.h"
28 #include "InsetMathSymbol.h"
29 #include "InsetMathUnknown.h"
30 #include "MathAutoCorrect.h"
31 #include "MathCompletionList.h"
32 #include "MathData.h"
33 #include "MathFactory.h"
34 #include "MathMacro.h"
35 #include "MathMacroArgument.h"
36 #include "MathParser.h"
37 #include "MathStream.h"
38 #include "MathSupport.h"
39
40 #include "Bidi.h"
41 #include "Buffer.h"
42 #include "BufferView.h"
43 #include "CoordCache.h"
44 #include "Cursor.h"
45 #include "CutAndPaste.h"
46 #include "DispatchResult.h"
47 #include "Encoding.h"
48 #include "FuncRequest.h"
49 #include "FuncStatus.h"
50 #include "LyXFunc.h"
51 #include "LyXRC.h"
52 #include "OutputParams.h"
53 #include "Text.h"
54
55 #include "frontends/Clipboard.h"
56 #include "frontends/Painter.h"
57 #include "frontends/Selection.h"
58
59 #include "support/lassert.h"
60 #include "support/debug.h"
61 #include "support/gettext.h"
62 #include "support/lstrings.h"
63 #include "support/textutils.h"
64 #include "support/docstream.h"
65
66 #include <algorithm>
67 #include <sstream>
68
69 using namespace std;
70 using namespace lyx::support;
71
72 namespace lyx {
73
74 using cap::copySelection;
75 using cap::grabAndEraseSelection;
76 using cap::cutSelection;
77 using cap::replaceSelection;
78 using cap::selClearOrDel;
79
80
81 InsetMathNest::InsetMathNest(idx_type nargs)
82         : cells_(nargs), lock_(false), mouse_hover_(false)
83 {}
84
85
86 InsetMathNest::InsetMathNest(InsetMathNest const & inset)
87         : InsetMath(inset), cells_(inset.cells_), lock_(inset.lock_),
88           mouse_hover_(false)
89 {}
90
91
92 InsetMathNest & InsetMathNest::operator=(InsetMathNest const & inset)
93 {
94         cells_ = inset.cells_;
95         lock_ = inset.lock_;
96         mouse_hover_ = false;
97         InsetMath::operator=(inset);
98         return *this;
99 }
100
101
102 InsetMath::idx_type InsetMathNest::nargs() const
103 {
104         return cells_.size();
105 }
106
107
108 void InsetMathNest::cursorPos(BufferView const & bv,
109                 CursorSlice const & sl, bool /*boundary*/,
110                 int & x, int & y) const
111 {
112 // FIXME: This is a hack. Ideally, the coord cache should not store
113 // absolute positions, but relative ones. This would mean to call
114 // setXY() not in MathData::draw(), but in the parent insets' draw()
115 // with the correctly adjusted x,y values. But this means that we'd have
116 // to touch all (math)inset's draw() methods. Right now, we'll store
117 // absolute value, and make them here relative, only to make them
118 // absolute again when actually drawing the cursor. What a mess.
119         LASSERT(&sl.inset() == this, /**/);
120         MathData const & ar = sl.cell();
121         CoordCache const & coord_cache = bv.coordCache();
122         if (!coord_cache.getArrays().has(&ar)) {
123                 // this can (semi-)legally happen if we just created this cell
124                 // and it never has been drawn before. So don't ASSERT.
125                 //lyxerr << "no cached data for array " << &ar << endl;
126                 x = 0;
127                 y = 0;
128                 return;
129         }
130         Point const pt = coord_cache.getArrays().xy(&ar);
131         if (!coord_cache.getInsets().has(this)) {
132                 // same as above
133                 //lyxerr << "no cached data for inset " << this << endl;
134                 x = 0;
135                 y = 0;
136                 return;
137         }
138         Point const pt2 = coord_cache.getInsets().xy(this);
139         //lyxerr << "retrieving position cache for MathData "
140         //      << pt.x_ << ' ' << pt.y_ << endl;
141         x = pt.x_ - pt2.x_ + ar.pos2x(&bv, sl.pos());
142         y = pt.y_ - pt2.y_;
143 //      lyxerr << "pt.y_ : " << pt.y_ << " pt2_.y_ : " << pt2.y_
144 //              << " asc: " << ascent() << "  des: " << descent()
145 //              << " ar.asc: " << ar.ascent() << " ar.des: " << ar.descent() << endl;
146         // move cursor visually into empty cells ("blue rectangles");
147         if (ar.empty())
148                 x += 2;
149 }
150
151
152 void InsetMathNest::metrics(MetricsInfo const & mi) const
153 {
154         MetricsInfo m = mi;
155         for (idx_type i = 0, n = nargs(); i != n; ++i) {
156                 Dimension dim;
157                 cell(i).metrics(m, dim);
158         }
159 }
160
161
162 bool InsetMathNest::idxNext(Cursor & cur) const
163 {
164         LASSERT(&cur.inset() == this, /**/);
165         if (cur.idx() == cur.lastidx())
166                 return false;
167         ++cur.idx();
168         cur.pos() = 0;
169         return true;
170 }
171
172
173 bool InsetMathNest::idxForward(Cursor & cur) const
174 {
175         return idxNext(cur);
176 }
177
178
179 bool InsetMathNest::idxPrev(Cursor & cur) const
180 {
181         LASSERT(&cur.inset() == this, /**/);
182         if (cur.idx() == 0)
183                 return false;
184         --cur.idx();
185         cur.pos() = cur.lastpos();
186         return true;
187 }
188
189
190 bool InsetMathNest::idxBackward(Cursor & cur) const
191 {
192         return idxPrev(cur);
193 }
194
195
196 bool InsetMathNest::idxFirst(Cursor & cur) const
197 {
198         LASSERT(&cur.inset() == this, /**/);
199         if (nargs() == 0)
200                 return false;
201         cur.idx() = 0;
202         cur.pos() = 0;
203         return true;
204 }
205
206
207 bool InsetMathNest::idxLast(Cursor & cur) const
208 {
209         LASSERT(&cur.inset() == this, /**/);
210         if (nargs() == 0)
211                 return false;
212         cur.idx() = cur.lastidx();
213         cur.pos() = cur.lastpos();
214         return true;
215 }
216
217
218 void InsetMathNest::dump() const
219 {
220         odocstringstream oss;
221         WriteStream os(oss);
222         os << "---------------------------------------------\n";
223         write(os);
224         os << "\n";
225         for (idx_type i = 0, n = nargs(); i != n; ++i)
226                 os << cell(i) << "\n";
227         os << "---------------------------------------------\n";
228         lyxerr << to_utf8(oss.str());
229 }
230
231
232 void InsetMathNest::draw(PainterInfo & pi, int x, int y) const
233 {
234 #if 0
235         if (lock_)
236                 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
237                                         Color_mathlockbg);
238 #endif
239         setPosCache(pi, x, y);
240 }
241
242
243 void InsetMathNest::drawSelection(PainterInfo & pi, int x, int y) const
244 {
245         BufferView & bv = *pi.base.bv;
246         // this should use the x/y values given, not the cached values
247         Cursor & cur = bv.cursor();
248         if (!cur.selection())
249                 return;
250         if (&cur.inset() != this)
251                 return;
252
253         // FIXME: hack to get position cache warm
254         bool const original_drawing_state = pi.pain.isDrawingEnabled();
255         pi.pain.setDrawingEnabled(false);
256         draw(pi, x, y);
257         pi.pain.setDrawingEnabled(original_drawing_state);
258
259         CursorSlice s1 = cur.selBegin();
260         CursorSlice s2 = cur.selEnd();
261
262         //lyxerr << "InsetMathNest::drawing selection: "
263         //      << " s1: " << s1 << " s2: " << s2 << endl;
264         if (s1.idx() == s2.idx()) {
265                 MathData const & c = cell(s1.idx());
266                 Geometry const & g = bv.coordCache().getArrays().geometry(&c);
267                 int x1 = g.pos.x_ + c.pos2x(pi.base.bv, s1.pos());
268                 int y1 = g.pos.y_ - g.dim.ascent();
269                 int x2 = g.pos.x_ + c.pos2x(pi.base.bv, s2.pos());
270                 int y2 = g.pos.y_ + g.dim.descent();
271                 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, Color_selection);
272         //lyxerr << "InsetMathNest::drawing selection 3: "
273         //      << " x1: " << x1 << " x2: " << x2
274         //      << " y1: " << y1 << " y2: " << y2 << endl;
275         } else {
276                 for (idx_type i = 0; i < nargs(); ++i) {
277                         if (idxBetween(i, s1.idx(), s2.idx())) {
278                                 MathData const & c = cell(i);
279                                 Geometry const & g = bv.coordCache().getArrays().geometry(&c);
280                                 int x1 = g.pos.x_;
281                                 int y1 = g.pos.y_ - g.dim.ascent();
282                                 int x2 = g.pos.x_ + g.dim.width();
283                                 int y2 = g.pos.y_ + g.dim.descent();
284                                 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, Color_selection);
285                         }
286                 }
287         }
288 }
289
290
291 void InsetMathNest::validate(LaTeXFeatures & features) const
292 {
293         for (idx_type i = 0; i < nargs(); ++i)
294                 cell(i).validate(features);
295 }
296
297
298 void InsetMathNest::replace(ReplaceData & rep)
299 {
300         for (idx_type i = 0; i < nargs(); ++i)
301                 cell(i).replace(rep);
302 }
303
304
305 bool InsetMathNest::contains(MathData const & ar) const
306 {
307         for (idx_type i = 0; i < nargs(); ++i)
308                 if (cell(i).contains(ar))
309                         return true;
310         return false;
311 }
312
313
314 bool InsetMathNest::lock() const
315 {
316         return lock_;
317 }
318
319
320 void InsetMathNest::lock(bool l)
321 {
322         lock_ = l;
323 }
324
325
326 bool InsetMathNest::isActive() const
327 {
328         return nargs() > 0;
329 }
330
331
332 MathData InsetMathNest::glue() const
333 {
334         MathData ar;
335         for (size_t i = 0; i < nargs(); ++i)
336                 ar.append(cell(i));
337         return ar;
338 }
339
340
341 void InsetMathNest::write(WriteStream & os) const
342 {
343         ModeSpecifier specifier(os, currentMode(), lockedMode());
344         docstring const latex_name = name();
345         os << '\\' << latex_name;
346         for (size_t i = 0; i < nargs(); ++i)
347                 os << '{' << cell(i) << '}';
348         if (nargs() == 0)
349                 os.pendingSpace(true);
350         if (lock_ && !os.latex()) {
351                 os << "\\lyxlock";
352                 os.pendingSpace(true);
353         }
354 }
355
356
357 void InsetMathNest::normalize(NormalStream & os) const
358 {
359         os << '[' << name();
360         for (size_t i = 0; i < nargs(); ++i)
361                 os << ' ' << cell(i);
362         os << ']';
363 }
364
365
366 int InsetMathNest::latex(odocstream & os, OutputParams const & runparams) const
367 {
368         WriteStream wi(os, runparams.moving_arg, true, runparams.dryrun,
369                         runparams.encoding);
370         write(wi);
371         return wi.line();
372 }
373
374
375 bool InsetMathNest::setMouseHover(bool mouse_hover)
376 {
377         mouse_hover_ = mouse_hover;
378         return true;
379 }
380
381
382 bool InsetMathNest::notifyCursorLeaves(Cursor const & /*old*/, Cursor & /*cur*/)
383 {
384         // FIXME: look here
385 #if 0
386         MathData & ar = cur.cell();
387         // remove base-only "scripts"
388         for (pos_type i = 0; i + 1 < ar.size(); ++i) {
389                 InsetMathScript * p = operator[](i).nucleus()->asScriptInset();
390                 if (p && p->nargs() == 1) {
391                         MathData ar = p->nuc();
392                         erase(i);
393                         insert(i, ar);
394                         cur.adjust(i, ar.size() - 1);
395                 }
396         }
397
398         // glue adjacent font insets of the same kind
399         for (pos_type i = 0; i + 1 < size(); ++i) {
400                 InsetMathFont * p = operator[](i).nucleus()->asFontInset();
401                 InsetMathFont const * q = operator[](i + 1)->asFontInset();
402                 if (p && q && p->name() == q->name()) {
403                         p->cell(0).append(q->cell(0));
404                         erase(i + 1);
405                         cur.adjust(i, -1);
406                 }
407         }
408 #endif
409         return false;
410 }
411
412
413 void InsetMathNest::handleFont
414         (Cursor & cur, docstring const & arg, char const * const font)
415 {
416         handleFont(cur, arg, from_ascii(font));
417 }
418
419
420 void InsetMathNest::handleFont(Cursor & cur, docstring const & arg,
421         docstring const & font)
422 {
423         cur.recordUndoSelection();
424
425         // this whole function is a hack and won't work for incremental font
426         // changes...
427         if (cur.inset().asInsetMath()->name() == font)
428                 cur.handleFont(to_utf8(font));
429         else
430                 handleNest(cur, createInsetMath(font), arg);
431 }
432
433
434 void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest)
435 {
436         handleNest(cur, nest, docstring());
437 }
438
439
440 void InsetMathNest::handleNest(Cursor & cur, MathAtom const & nest,
441         docstring const & arg)
442 {
443         CursorSlice i1 = cur.selBegin();
444         CursorSlice i2 = cur.selEnd();
445         if (!i1.inset().asInsetMath())
446                 return;
447         if (i1.idx() == i2.idx()) {
448                 // the easy case where only one cell is selected
449                 cur.handleNest(nest);
450                 cur.insert(arg);
451                 return;
452         }
453
454         // multiple selected cells in a simple non-grid inset
455         if (i1.asInsetMath()->nrows() == 0 || i1.asInsetMath()->ncols() == 0) {
456                 for (idx_type i = i1.idx(); i <= i2.idx(); ++i) {
457                         // select cell
458                         cur.idx() = i;
459                         cur.pos() = 0;
460                         cur.resetAnchor();
461                         cur.pos() = cur.lastpos();
462                         cur.setSelection();
463
464                         // change font of cell
465                         cur.handleNest(nest);
466                         cur.insert(arg);
467
468                         // cur is in the font inset now. If the loop continues,
469                         // we need to get outside again for the next cell
470                         if (i + 1 <= i2.idx())
471                                 cur.pop_back();
472                 }
473                 return;
474         }
475
476         // the complicated case with multiple selected cells in a grid
477         row_type r1, r2;
478         col_type c1, c2;
479         cap::region(i1, i2, r1, r2, c1, c2);
480         for (row_type row = r1; row <= r2; ++row) {
481                 for (col_type col = c1; col <= c2; ++col) {
482                         // select cell
483                         cur.idx() = i1.asInsetMath()->index(row, col);
484                         cur.pos() = 0;
485                         cur.resetAnchor();
486                         cur.pos() = cur.lastpos();
487                         cur.setSelection();
488
489                         //
490                         cur.handleNest(nest);
491                         cur.insert(arg);
492
493                         // cur is in the font inset now. If the loop continues,
494                         // we need to get outside again for the next cell
495                         if (col + 1 <= c2 || row + 1 <= r2)
496                                 cur.pop_back();
497                 }
498         }
499 }
500
501
502 void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg)
503 {
504         cur.recordUndoSelection();
505         Font font;
506         bool b;
507         font.fromString(to_utf8(arg), b);
508         if (font.fontInfo().color() != Color_inherit &&
509             font.fontInfo().color() != Color_ignore)
510                 handleNest(cur, MathAtom(new InsetMathColor(true, font.fontInfo().color())));
511
512         // FIXME: support other font changes here as well?
513 }
514
515
516 void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
517 {
518         //lyxerr << "InsetMathNest: request: " << cmd << endl;
519
520         Parse::flags parseflg = Parse::QUIET | Parse::USETEXT;
521
522         switch (cmd.action) {
523
524         case LFUN_CLIPBOARD_PASTE:
525                 parseflg |= Parse::VERBATIM;
526                 // fall through
527         case LFUN_PASTE: {
528                 if (cur.currentMode() <= TEXT_MODE)
529                         parseflg |= Parse::TEXTMODE;
530                 cur.recordUndoSelection();
531                 cur.message(_("Paste"));
532                 replaceSelection(cur);
533                 docstring topaste;
534                 if (cmd.argument().empty() && !theClipboard().isInternal())
535                         topaste = theClipboard().getAsText();
536                 else {
537                         size_t n = 0;
538                         idocstringstream is(cmd.argument());
539                         is >> n;
540                         topaste = cap::selection(n);
541                 }
542                 cur.niceInsert(topaste, parseflg);
543                 cur.clearSelection(); // bug 393
544                 cur.finishUndo();
545                 break;
546         }
547
548         case LFUN_CUT:
549                 cur.recordUndo();
550                 cutSelection(cur, true, true);
551                 cur.message(_("Cut"));
552                 // Prevent stale position >= size crash
553                 // Probably not necessary anymore, see eraseSelection (gb 2005-10-09)
554                 cur.normalize();
555                 break;
556
557         case LFUN_COPY:
558                 copySelection(cur);
559                 cur.message(_("Copy"));
560                 break;
561
562         case LFUN_MOUSE_PRESS:
563                 lfunMousePress(cur, cmd);
564                 break;
565
566         case LFUN_MOUSE_MOTION:
567                 lfunMouseMotion(cur, cmd);
568                 break;
569
570         case LFUN_MOUSE_RELEASE:
571                 lfunMouseRelease(cur, cmd);
572                 break;
573
574         case LFUN_FINISHED_LEFT: // in math, left is backwards
575         case LFUN_FINISHED_BACKWARD:
576                 cur.bv().cursor() = cur;
577                 break;
578
579         case LFUN_FINISHED_RIGHT: // in math, right is forward
580         case LFUN_FINISHED_FORWARD:
581                 ++cur.pos();
582                 cur.bv().cursor() = cur;
583                 break;
584
585         case LFUN_CHAR_RIGHT:
586         case LFUN_CHAR_LEFT:
587         case LFUN_CHAR_BACKWARD:
588         case LFUN_CHAR_FORWARD:
589                 cur.updateFlags(Update::Decoration | Update::FitCursor);
590         case LFUN_CHAR_RIGHT_SELECT:
591         case LFUN_CHAR_LEFT_SELECT:
592         case LFUN_CHAR_BACKWARD_SELECT:
593         case LFUN_CHAR_FORWARD_SELECT: {
594                 // are we in a selection?
595                 bool select = (cmd.action == LFUN_CHAR_RIGHT_SELECT
596                                            || cmd.action == LFUN_CHAR_LEFT_SELECT
597                                            || cmd.action == LFUN_CHAR_BACKWARD_SELECT
598                                            || cmd.action == LFUN_CHAR_FORWARD_SELECT);
599                 // are we moving forward or backwards?
600                 // If the command was RIGHT or LEFT, then whether we're moving forward
601                 // or backwards depends on the cursor movement mode (logical or visual):
602                 //  * in visual mode, since math is always LTR, right -> forward,
603                 //    left -> backwards
604                 //  * in logical mode, the mapping is determined by the
605                 //    reverseDirectionNeeded() function
606
607                 bool forward;
608                 FuncCode finish_lfun;
609
610                 if (cmd.action == LFUN_CHAR_FORWARD
611                                 || cmd.action == LFUN_CHAR_FORWARD_SELECT) {
612                         forward = true;
613                         finish_lfun = LFUN_FINISHED_FORWARD;
614                 }
615                 else if (cmd.action == LFUN_CHAR_BACKWARD
616                                 || cmd.action == LFUN_CHAR_BACKWARD_SELECT) {
617                         forward = false;
618                         finish_lfun = LFUN_FINISHED_BACKWARD;
619                 }
620                 else {
621                         bool right = (cmd.action == LFUN_CHAR_RIGHT_SELECT
622                                                   || cmd.action == LFUN_CHAR_RIGHT);
623                         if (lyxrc.visual_cursor || !reverseDirectionNeeded(cur))
624                                 forward = right;
625                         else
626                                 forward = !right;
627
628                         if (right)
629                                 finish_lfun = LFUN_FINISHED_RIGHT;
630                         else
631                                 finish_lfun = LFUN_FINISHED_LEFT;
632                 }
633                 // Now that we know exactly what we want to do, let's do it!
634                 cur.selHandle(select);
635                 cur.clearTargetX();
636                 cur.macroModeClose();
637                 // try moving forward or backwards as necessary...
638                 if (!(forward ? cursorMathForward(cur) : cursorMathBackward(cur))) {
639                         // ... and if movement failed, then finish forward or backwards
640                         // as necessary
641                         cmd = FuncRequest(finish_lfun);
642                         cur.undispatched();
643                 }
644                 break;
645         }
646
647         case LFUN_DOWN:
648         case LFUN_UP:
649                 cur.updateFlags(Update::Decoration | Update::FitCursor);
650         case LFUN_DOWN_SELECT:
651         case LFUN_UP_SELECT: {
652                 // close active macro
653                 if (cur.inMacroMode()) {
654                         cur.macroModeClose();
655                         break;
656                 }
657
658                 // stop/start the selection
659                 bool select = cmd.action == LFUN_DOWN_SELECT ||
660                         cmd.action == LFUN_UP_SELECT;
661                 cur.selHandle(select);
662
663                 // go up/down
664                 bool up = cmd.action == LFUN_UP || cmd.action == LFUN_UP_SELECT;
665                 bool successful = cur.upDownInMath(up);
666                 if (successful)
667                         break;
668
669                 if (cur.fixIfBroken())
670                         // FIXME: Something bad happened. We pass the corrected Cursor
671                         // instead of letting things go worse.
672                         break;
673
674                 // We did not manage to move the cursor.
675                 cur.undispatched();
676                 break;
677         }
678
679         case LFUN_MOUSE_DOUBLE:
680         case LFUN_MOUSE_TRIPLE:
681         case LFUN_WORD_SELECT:
682                 cur.pos() = 0;
683                 cur.idx() = 0;
684                 cur.resetAnchor();
685                 cur.setSelection(true);
686                 cur.pos() = cur.lastpos();
687                 cur.idx() = cur.lastidx();
688                 break;
689
690         case LFUN_PARAGRAPH_UP:
691         case LFUN_PARAGRAPH_DOWN:
692                 cur.updateFlags(Update::Decoration | Update::FitCursor);
693         case LFUN_PARAGRAPH_UP_SELECT:
694         case LFUN_PARAGRAPH_DOWN_SELECT:
695                 break;
696
697         case LFUN_LINE_BEGIN:
698         case LFUN_WORD_BACKWARD:
699         case LFUN_WORD_LEFT:
700                 cur.updateFlags(Update::Decoration | Update::FitCursor);
701         case LFUN_LINE_BEGIN_SELECT:
702         case LFUN_WORD_BACKWARD_SELECT:
703         case LFUN_WORD_LEFT_SELECT:
704                 cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT ||
705                                 cmd.action == LFUN_WORD_LEFT_SELECT ||
706                                 cmd.action == LFUN_LINE_BEGIN_SELECT);
707                 cur.macroModeClose();
708                 if (cur.pos() != 0) {
709                         cur.pos() = 0;
710                 } else if (cur.col() != 0) {
711                         cur.idx() -= cur.col();
712                         cur.pos() = 0;
713                 } else if (cur.idx() != 0) {
714                         cur.idx() = 0;
715                         cur.pos() = 0;
716                 } else {
717                         cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
718                         cur.undispatched();
719                 }
720                 break;
721
722         case LFUN_WORD_FORWARD:
723         case LFUN_WORD_RIGHT:
724         case LFUN_LINE_END:
725                 cur.updateFlags(Update::Decoration | Update::FitCursor);
726         case LFUN_WORD_FORWARD_SELECT:
727         case LFUN_WORD_RIGHT_SELECT:
728         case LFUN_LINE_END_SELECT:
729                 cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT ||
730                                 cmd.action == LFUN_WORD_RIGHT_SELECT ||
731                                 cmd.action == LFUN_LINE_END_SELECT);
732                 cur.macroModeClose();
733                 cur.clearTargetX();
734                 if (cur.pos() != cur.lastpos()) {
735                         cur.pos() = cur.lastpos();
736                 } else if (ncols() && (cur.col() != cur.lastcol())) {
737                         cur.idx() = cur.idx() - cur.col() + cur.lastcol();
738                         cur.pos() = cur.lastpos();
739                 } else if (cur.idx() != cur.lastidx()) {
740                         cur.idx() = cur.lastidx();
741                         cur.pos() = cur.lastpos();
742                 } else {
743                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
744                         cur.undispatched();
745                 }
746                 break;
747
748         case LFUN_CELL_FORWARD:
749                 cur.updateFlags(Update::Decoration | Update::FitCursor);
750                 cur.inset().idxNext(cur);
751                 break;
752
753         case LFUN_CELL_BACKWARD:
754                 cur.updateFlags(Update::Decoration | Update::FitCursor);
755                 cur.inset().idxPrev(cur);
756                 break;
757
758         case LFUN_WORD_DELETE_BACKWARD:
759         case LFUN_CHAR_DELETE_BACKWARD:
760                 if (cur.pos() == 0)
761                         // May affect external cell:
762                         cur.recordUndoInset();
763                 else
764                         cur.recordUndoSelection();
765                 // if the inset can not be removed from within, delete it
766                 if (!cur.backspace()) {
767                         FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
768                         cur.innerText()->dispatch(cur, cmd);
769                 }
770                 break;
771
772         case LFUN_WORD_DELETE_FORWARD:
773         case LFUN_CHAR_DELETE_FORWARD:
774                 if (cur.pos() == cur.lastpos())
775                         // May affect external cell:
776                         cur.recordUndoInset();
777                 else
778                         cur.recordUndoSelection();
779                 // if the inset can not be removed from within, delete it
780                 if (!cur.erase()) {
781                         FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
782                         cur.innerText()->dispatch(cur, cmd);
783                 }
784                 break;
785
786         case LFUN_ESCAPE:
787                 if (cur.selection())
788                         cur.clearSelection();
789                 else  {
790                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
791                         cur.undispatched();
792                 }
793                 break;
794
795         // 'Locks' the math inset. A 'locked' math inset behaves as a unit
796         // that is traversed by a single <CursorLeft>/<CursorRight>.
797         case LFUN_INSET_TOGGLE:
798                 cur.recordUndo();
799                 lock(!lock());
800                 cur.popForward();
801                 break;
802
803         case LFUN_SELF_INSERT:
804                 if (cmd.argument().size() != 1) {
805                         cur.recordUndoSelection();
806                         docstring const arg = cmd.argument();
807                         if (!interpretString(cur, arg))
808                                 cur.insert(arg);
809                         break;
810                 }
811                 // Don't record undo steps if we are in macro mode and
812                 // cmd.argument is the next character of the macro name.
813                 // Otherwise we'll get an invalid cursor if we undo after
814                 // the macro was finished and the macro is a known command,
815                 // e.g. sqrt. Cursor::macroModeClose replaces in this case
816                 // the InsetMathUnknown with name "frac" by an empty
817                 // InsetMathFrac -> a pos value > 0 is invalid.
818                 // A side effect is that an undo before the macro is finished
819                 // undoes the complete macro, not only the last character.
820                 if (!cur.inMacroMode()) {
821                         MathMacro const * macro = 0;
822                         if (cur.pos() > 0 && cmd.argument() != "\\")
823                                 macro = cur.inset().asInsetMath()->asMacro();
824                         
825                         if (!macro)
826                                 cur.recordUndoSelection();
827                 }
828
829                 // spacial handling of space. If we insert an inset
830                 // via macro mode, we want to put the cursor inside it
831                 // if relevant. Think typing "\frac<space>".
832                 if (cmd.argument()[0] == ' '
833                     && cur.inMacroMode() && cur.macroName() != "\\"
834                     && cur.macroModeClose()) {
835                         MathAtom const atom = cur.prevAtom();
836                         if (atom->asNestInset() && atom->isActive()) {
837                                 cur.posBackward();
838                                 cur.pushBackward(*cur.nextInset());
839                         }
840                 } else if (!interpretChar(cur, cmd.argument()[0])) {
841                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
842                         cur.undispatched();
843                 }
844                 break;
845
846         //case LFUN_SERVER_GET_XY:
847         //      break;
848
849         case LFUN_SERVER_SET_XY: {
850                 lyxerr << "LFUN_SERVER_SET_XY broken!" << endl;
851                 int x = 0;
852                 int y = 0;
853                 istringstream is(to_utf8(cmd.argument()));
854                 is >> x >> y;
855                 cur.setScreenPos(x, y);
856                 break;
857         }
858
859         // Special casing for superscript in case of LyX handling
860         // dead-keys:
861         case LFUN_ACCENT_CIRCUMFLEX:
862                 if (cmd.argument().empty()) {
863                         // do superscript if LyX handles
864                         // deadkeys
865                         cur.recordUndoSelection();
866                         script(cur, true, grabAndEraseSelection(cur));
867                 }
868                 break;
869
870         case LFUN_ACCENT_UMLAUT:
871         case LFUN_ACCENT_ACUTE:
872         case LFUN_ACCENT_GRAVE:
873         case LFUN_ACCENT_BREVE:
874         case LFUN_ACCENT_DOT:
875         case LFUN_ACCENT_MACRON:
876         case LFUN_ACCENT_CARON:
877         case LFUN_ACCENT_TILDE:
878         case LFUN_ACCENT_CEDILLA:
879         case LFUN_ACCENT_CIRCLE:
880         case LFUN_ACCENT_UNDERDOT:
881         case LFUN_ACCENT_TIE:
882         case LFUN_ACCENT_OGONEK:
883         case LFUN_ACCENT_HUNGARIAN_UMLAUT:
884                 break;
885
886         //  Math fonts
887         case LFUN_TEXTSTYLE_APPLY:
888         case LFUN_TEXTSTYLE_UPDATE:
889                 handleFont2(cur, cmd.argument());
890                 break;
891
892         case LFUN_FONT_BOLD:
893                 if (currentMode() <= TEXT_MODE)
894                         handleFont(cur, cmd.argument(), "textbf");
895                 else
896                         handleFont(cur, cmd.argument(), "mathbf");
897                 break;
898         case LFUN_FONT_BOLDSYMBOL:
899                 if (currentMode() <= TEXT_MODE)
900                         handleFont(cur, cmd.argument(), "textbf");
901                 else
902                         handleFont(cur, cmd.argument(), "boldsymbol");
903                 break;
904         case LFUN_FONT_SANS:
905                 if (currentMode() <= TEXT_MODE)
906                         handleFont(cur, cmd.argument(), "textsf");
907                 else
908                         handleFont(cur, cmd.argument(), "mathsf");
909                 break;
910         case LFUN_FONT_EMPH:
911                 if (currentMode() <= TEXT_MODE)
912                         handleFont(cur, cmd.argument(), "emph");
913                 else
914                         handleFont(cur, cmd.argument(), "mathcal");
915                 break;
916         case LFUN_FONT_ROMAN:
917                 if (currentMode() <= TEXT_MODE)
918                         handleFont(cur, cmd.argument(), "textrm");
919                 else
920                         handleFont(cur, cmd.argument(), "mathrm");
921                 break;
922         case LFUN_FONT_TYPEWRITER:
923                 if (currentMode() <= TEXT_MODE)
924                         handleFont(cur, cmd.argument(), "texttt");
925                 else
926                         handleFont(cur, cmd.argument(), "mathtt");
927                 break;
928         case LFUN_FONT_FRAK:
929                 handleFont(cur, cmd.argument(), "mathfrak");
930                 break;
931         case LFUN_FONT_ITAL:
932                 if (currentMode() <= TEXT_MODE)
933                         handleFont(cur, cmd.argument(), "textit");
934                 else
935                         handleFont(cur, cmd.argument(), "mathit");
936                 break;
937         case LFUN_FONT_NOUN:
938                 if (currentMode() <= TEXT_MODE)
939                         // FIXME: should be "noun"
940                         handleFont(cur, cmd.argument(), "textsc");
941                 else
942                         handleFont(cur, cmd.argument(), "mathbb");
943                 break;
944         case LFUN_FONT_DEFAULT:
945                 handleFont(cur, cmd.argument(), "textnormal");
946                 break;
947
948         case LFUN_MATH_MODE: {
949 #if 1
950                 // ignore math-mode on when already in math mode
951                 if (currentMode() == Inset::MATH_MODE && cmd.argument() == "on")
952                         break;
953                 cur.recordUndoSelection();
954                 cur.macroModeClose();
955                 docstring const save_selection = grabAndEraseSelection(cur);
956                 selClearOrDel(cur);
957                 //cur.plainInsert(MathAtom(new InsetMathMBox(cur.bv())));
958                 if (currentMode() <= Inset::TEXT_MODE)
959                         cur.plainInsert(MathAtom(new InsetMathEnsureMath));
960                 else
961                         cur.plainInsert(MathAtom(new InsetMathBox(from_ascii("mbox"))));
962                 cur.posBackward();
963                 cur.pushBackward(*cur.nextInset());
964                 cur.niceInsert(save_selection);
965 #else
966                 if (currentMode() == Inset::TEXT_MODE) {
967                         cur.recordUndoSelection();
968                         cur.niceInsert(MathAtom(new InsetMathHull("simple")));
969                         cur.message(_("create new math text environment ($...$)"));
970                 } else {
971                         handleFont(cur, cmd.argument(), "textrm");
972                         cur.message(_("entered math text mode (textrm)"));
973                 }
974 #endif
975                 break;
976         }
977
978         case LFUN_REGEXP_MODE: {
979                 InsetMathHull * i = dynamic_cast<InsetMathHull *>(cur.inset().asInsetMath());
980                 if (i && i->getType() == hullRegexp) {
981                         cur.message(_("Already in regexp mode"));
982                         break;
983                 }
984                 cur.macroModeClose();
985                 docstring const save_selection = grabAndEraseSelection(cur);
986                 selClearOrDel(cur);
987                 cur.plainInsert(MathAtom(new InsetMathHull(hullRegexp)));
988                 cur.posBackward();
989                 cur.pushBackward(*cur.nextInset());
990                 cur.niceInsert(save_selection);
991                 cur.message(_("Regexp editor mode"));
992                 break;
993         }
994
995         case LFUN_MATH_FONT_STYLE: {
996                 FuncRequest fr = FuncRequest(LFUN_MATH_INSERT, '\\' + cmd.argument());
997                 doDispatch(cur, fr);
998                 break;
999         }
1000
1001         case LFUN_MATH_SIZE: {
1002                 FuncRequest fr = FuncRequest(LFUN_MATH_INSERT, cmd.argument());
1003                 doDispatch(cur, fr);
1004                 break;
1005         }
1006
1007         case LFUN_MATH_MATRIX: {
1008                 cur.recordUndo();
1009                 unsigned int m = 1;
1010                 unsigned int n = 1;
1011                 docstring v_align;
1012                 docstring h_align;
1013                 idocstringstream is(cmd.argument());
1014                 is >> m >> n >> v_align >> h_align;
1015                 if (m < 1)
1016                         m = 1;
1017                 if (n < 1)
1018                         n = 1;
1019                 v_align += 'c';
1020                 cur.niceInsert(
1021                         MathAtom(new InsetMathArray(from_ascii("array"), m, n, (char)v_align[0], h_align)));
1022                 break;
1023         }
1024
1025         case LFUN_MATH_DELIM: {
1026                 docstring ls;
1027                 docstring rs = split(cmd.argument(), ls, ' ');
1028                 // Reasonable default values
1029                 if (ls.empty())
1030                         ls = '(';
1031                 if (rs.empty())
1032                         rs = ')';
1033                 cur.recordUndo();
1034                 cur.handleNest(MathAtom(new InsetMathDelim(ls, rs)));
1035                 break;
1036         }
1037
1038         case LFUN_MATH_BIGDELIM: {
1039                 docstring const lname  = from_utf8(cmd.getArg(0));
1040                 docstring const ldelim = from_utf8(cmd.getArg(1));
1041                 docstring const rname  = from_utf8(cmd.getArg(2));
1042                 docstring const rdelim = from_utf8(cmd.getArg(3));
1043                 latexkeys const * l = in_word_set(lname);
1044                 bool const have_l = l && l->inset == "big" &&
1045                                     InsetMathBig::isBigInsetDelim(ldelim);
1046                 l = in_word_set(rname);
1047                 bool const have_r = l && l->inset == "big" &&
1048                                     InsetMathBig::isBigInsetDelim(rdelim);
1049                 // We mimic LFUN_MATH_DELIM in case we have an empty left
1050                 // or right delimiter.
1051                 if (have_l || have_r) {
1052                         cur.recordUndo();
1053                         docstring const selection = grabAndEraseSelection(cur);
1054                         selClearOrDel(cur);
1055                         if (have_l)
1056                                 cur.insert(MathAtom(new InsetMathBig(lname,
1057                                                                 ldelim)));
1058                         cur.niceInsert(selection);
1059                         if (have_r)
1060                                 cur.insert(MathAtom(new InsetMathBig(rname,
1061                                                                 rdelim)));
1062                 }
1063                 // Don't call cur.undispatched() if we did nothing, this would
1064                 // lead to infinite recursion via Text::dispatch().
1065                 break;
1066         }
1067
1068         case LFUN_SPACE_INSERT:
1069                 cur.recordUndoSelection();
1070                 cur.insert(MathAtom(new InsetMathSpace));
1071                 break;
1072
1073         case LFUN_MATH_SPACE:
1074                 cur.recordUndoSelection();
1075                 if (cmd.argument().empty())
1076                         cur.insert(MathAtom(new InsetMathSpace));
1077                 else {
1078                         string const name = cmd.getArg(0);
1079                         string const len = cmd.getArg(1);
1080                         cur.insert(MathAtom(new InsetMathSpace(name, len)));
1081                 }
1082                 break;
1083
1084         case LFUN_ERT_INSERT:
1085                 // interpret this as if a backslash was typed
1086                 cur.recordUndo();
1087                 interpretChar(cur, '\\');
1088                 break;
1089
1090         case LFUN_MATH_SUBSCRIPT:
1091                 // interpret this as if a _ was typed
1092                 cur.recordUndoSelection();
1093                 interpretChar(cur, '_');
1094                 break;
1095
1096         case LFUN_MATH_SUPERSCRIPT:
1097                 // interpret this as if a ^ was typed
1098                 cur.recordUndoSelection();
1099                 interpretChar(cur, '^');
1100                 break;
1101
1102         case LFUN_MATH_MACRO_FOLD:
1103         case LFUN_MATH_MACRO_UNFOLD: {
1104                 Cursor it = cur;
1105                 bool fold = cmd.action == LFUN_MATH_MACRO_FOLD;
1106                 bool found = findMacroToFoldUnfold(it, fold);
1107                 if (found) {
1108                         MathMacro * macro = it.nextInset()->asInsetMath()->asMacro();
1109                         cur.recordUndoInset();
1110                         if (fold)
1111                                 macro->fold(cur);
1112                         else
1113                                 macro->unfold(cur);
1114                 }
1115                 break;
1116         }
1117
1118         case LFUN_QUOTE_INSERT:
1119                 // interpret this as if a straight " was typed
1120                 cur.recordUndoSelection();
1121                 interpretChar(cur, '\"');
1122                 break;
1123
1124 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
1125 // handling such that "self-insert" works on "arbitrary stuff" too, and
1126 // math-insert only handles special math things like "matrix".
1127         case LFUN_MATH_INSERT: {
1128                 cur.recordUndoSelection();
1129                 if (cmd.argument() == "^" || cmd.argument() == "_")
1130                         interpretChar(cur, cmd.argument()[0]);
1131                 else {
1132                         MathData ar;
1133                         asArray(cmd.argument(), ar);
1134                         if (cur.selection() && ar.size() == 1
1135                             && ar[0]->asNestInset()
1136                             && ar[0]->asNestInset()->nargs() > 1)
1137                                 handleNest(cur, ar[0]);
1138                         else
1139                                 cur.niceInsert(cmd.argument());
1140                 }
1141                 break;
1142                 }
1143
1144         case LFUN_DIALOG_SHOW_NEW_INSET: {
1145                 docstring const & name = cmd.argument();
1146                 string data;
1147                 if (name == "ref") {
1148                         InsetMathRef tmp(name);
1149                         data = tmp.createDialogStr(to_utf8(name));
1150                 } else if (name == "mathspace") {
1151                         InsetMathSpace tmp;
1152                         data = tmp.createDialogStr();
1153                 }
1154                 cur.bv().showDialog(to_utf8(name), data);
1155                 break;
1156         }
1157
1158         case LFUN_INSET_INSERT: {
1159                 MathData ar;
1160                 if (createInsetMath_fromDialogStr(cmd.argument(), ar)) {
1161                         cur.recordUndoSelection();
1162                         cur.insert(ar);
1163                 } else
1164                         cur.undispatched();
1165                 break;
1166         }
1167         case LFUN_INSET_DISSOLVE:
1168                 if (!asHullInset()) {
1169                         cur.recordUndoInset();
1170                         cur.pullArg();
1171                 }
1172                 break;
1173
1174         default:
1175                 InsetMath::doDispatch(cur, cmd);
1176                 break;
1177         }
1178 }
1179
1180
1181 bool InsetMathNest::findMacroToFoldUnfold(Cursor & it, bool fold) const {
1182         // look for macro to open/close, but stay in mathed
1183         for (; !it.empty(); it.pop_back()) {
1184
1185                 // go backward through the current cell
1186                 Inset * inset = it.nextInset();
1187                 while (inset && inset->asInsetMath()) {
1188                         MathMacro * macro = inset->asInsetMath()->asMacro();
1189                         if (macro) {
1190                                 // found the an macro to open/close?
1191                                 if (macro->folded() != fold)
1192                                         return true;
1193
1194                                 // Wrong folding state.
1195                                 // If this was the first we see in this slice, look further left,
1196                                 // otherwise go up.
1197                                 if (inset != it.nextInset())
1198                                         break;
1199                         }
1200
1201                         // go up if this was the left most position
1202                         if (it.pos() == 0)
1203                                 break;
1204
1205                         // go left
1206                         it.pos()--;
1207                         inset = it.nextInset();
1208                 }
1209         }
1210
1211         return false;
1212 }
1213
1214
1215 bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
1216                 FuncStatus & flag) const
1217 {
1218         // the font related toggles
1219         //string tc = "mathnormal";
1220         bool ret = true;
1221         string const arg = to_utf8(cmd.argument());
1222         switch (cmd.action) {
1223         case LFUN_TABULAR_FEATURE:
1224                 flag.setEnabled(false);
1225                 break;
1226 #if 0
1227         case LFUN_TABULAR_FEATURE:
1228                 // FIXME: check temporarily disabled
1229                 // valign code
1230                 char align = mathcursor::valign();
1231                 if (align == '\0') {
1232                         enable = false;
1233                         break;
1234                 }
1235                 if (cmd.argument().empty()) {
1236                         flag.clear();
1237                         break;
1238                 }
1239                 if (!contains("tcb", cmd.argument()[0])) {
1240                         enable = false;
1241                         break;
1242                 }
1243                 flag.setOnOff(cmd.argument()[0] == align);
1244                 break;
1245 #endif
1246         /// We have to handle them since 1.4 blocks all unhandled actions
1247         case LFUN_FONT_ITAL:
1248         case LFUN_FONT_BOLD:
1249         case LFUN_FONT_BOLDSYMBOL:
1250         case LFUN_FONT_SANS:
1251         case LFUN_FONT_EMPH:
1252         case LFUN_FONT_TYPEWRITER:
1253         case LFUN_FONT_NOUN:
1254         case LFUN_FONT_ROMAN:
1255         case LFUN_FONT_DEFAULT:
1256                 flag.setEnabled(true);
1257                 break;
1258
1259         // we just need to be in math mode to enable that
1260         case LFUN_MATH_SIZE:
1261         case LFUN_MATH_SPACE:
1262         case LFUN_MATH_LIMITS:
1263         case LFUN_MATH_EXTERN:
1264                 flag.setEnabled(true);
1265                 break;
1266
1267         case LFUN_FONT_FRAK:
1268                 flag.setEnabled(currentMode() != TEXT_MODE);
1269                 break;
1270
1271         case LFUN_MATH_FONT_STYLE: {
1272                 bool const textarg =
1273                         arg == "textbf"   || arg == "textsf" ||
1274                         arg == "textrm"   || arg == "textmd" ||
1275                         arg == "textit"   || arg == "textsc" ||
1276                         arg == "textsl"   || arg == "textup" ||
1277                         arg == "texttt"   || arg == "textbb" ||
1278                         arg == "textnormal";
1279                 flag.setEnabled(currentMode() != TEXT_MODE || textarg);
1280                 break;
1281         }
1282
1283         case LFUN_MATH_INSERT:
1284                 flag.setEnabled(currentMode() != TEXT_MODE);
1285                 break;
1286
1287         case LFUN_MATH_MATRIX:
1288                 flag.setEnabled(currentMode() == MATH_MODE);
1289                 break;
1290
1291         case LFUN_INSET_INSERT: {
1292                 // Don't test createMathInset_fromDialogStr(), since
1293                 // getStatus is not called with a valid reference and the
1294                 // dialog would not be applyable.
1295                 string const name = cmd.getArg(0);
1296                 flag.setEnabled(name == "ref" || name == "mathspace");
1297                 break;
1298         }
1299
1300         case LFUN_MATH_DELIM:
1301         case LFUN_MATH_BIGDELIM:
1302                 // Don't do this with multi-cell selections
1303                 flag.setEnabled(cur.selBegin().idx() == cur.selEnd().idx());
1304                 break;
1305
1306         case LFUN_MATH_MACRO_FOLD:
1307         case LFUN_MATH_MACRO_UNFOLD: {
1308                 Cursor it = cur;
1309                 bool found = findMacroToFoldUnfold(it, cmd.action == LFUN_MATH_MACRO_FOLD);
1310                 flag.setEnabled(found);
1311                 break;
1312         }
1313
1314         case LFUN_SPECIALCHAR_INSERT:
1315                 // FIXME: These would probably make sense in math-text mode
1316                 flag.setEnabled(false);
1317                 break;
1318
1319         case LFUN_INSET_DISSOLVE:
1320                 flag.setEnabled(!asHullInset());
1321                 break;
1322
1323         default:
1324                 ret = false;
1325                 break;
1326         }
1327         return ret;
1328 }
1329
1330
1331 void InsetMathNest::edit(Cursor & cur, bool front, EntryDirection entry_from)
1332 {
1333         cur.push(*this);
1334         bool enter_front = (entry_from == Inset::ENTRY_DIRECTION_RIGHT ||
1335                 (entry_from == Inset::ENTRY_DIRECTION_IGNORE && front));
1336         cur.idx() = enter_front ? 0 : cur.lastidx();
1337         cur.pos() = enter_front ? 0 : cur.lastpos();
1338         cur.resetAnchor();
1339         //lyxerr << "InsetMathNest::edit, cur:\n" << cur << endl;
1340 }
1341
1342
1343 Inset * InsetMathNest::editXY(Cursor & cur, int x, int y)
1344 {
1345         int idx_min = 0;
1346         int dist_min = 1000000;
1347         for (idx_type i = 0, n = nargs(); i != n; ++i) {
1348                 int const d = cell(i).dist(cur.bv(), x, y);
1349                 if (d < dist_min) {
1350                         dist_min = d;
1351                         idx_min = i;
1352                 }
1353         }
1354         MathData & ar = cell(idx_min);
1355         cur.push(*this);
1356         cur.idx() = idx_min;
1357         cur.pos() = ar.x2pos(&cur.bv(), x - ar.xo(cur.bv()));
1358
1359         //lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
1360         if (dist_min == 0) {
1361                 // hit inside cell
1362                 for (pos_type i = 0, n = ar.size(); i < n; ++i)
1363                         if (ar[i]->covers(cur.bv(), x, y))
1364                                 return ar[i].nucleus()->editXY(cur, x, y);
1365         }
1366         return this;
1367 }
1368
1369
1370 void InsetMathNest::lfunMousePress(Cursor & cur, FuncRequest & cmd)
1371 {
1372         //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
1373         BufferView & bv = cur.bv();
1374         bool do_selection = cmd.button() == mouse_button::button1
1375                 && cmd.argument() == "region-select";
1376         bv.mouseSetCursor(cur, do_selection);
1377         if (cmd.button() == mouse_button::button1) {
1378                 //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
1379                 // Update the cursor update flags as needed:
1380                 //
1381                 // Update::Decoration: tells to update the decoration
1382                 //                     (visual box corners that define
1383                 //                     the inset)/
1384                 // Update::FitCursor: adjust the screen to the cursor
1385                 //                    position if needed
1386                 // cur.result().update(): don't overwrite previously set flags.
1387                 cur.updateFlags(Update::Decoration | Update::FitCursor
1388                                 | cur.result().update());
1389         } else if (cmd.button() == mouse_button::button2) {
1390                 if (cap::selection()) {
1391                         // See comment in Text::dispatch why we do this
1392                         cap::copySelectionToStack();
1393                         cmd = FuncRequest(LFUN_PASTE, "0");
1394                         doDispatch(bv.cursor(), cmd);
1395                 } else {
1396                         MathData ar;
1397                         asArray(theSelection().get(), ar);
1398                         bv.cursor().insert(ar);
1399                 }
1400         }
1401 }
1402
1403
1404 void InsetMathNest::lfunMouseMotion(Cursor & cur, FuncRequest & cmd)
1405 {
1406         // only select with button 1
1407         if (cmd.button() == mouse_button::button1) {
1408                 Cursor & bvcur = cur.bv().cursor();
1409                 if (bvcur.anchor_.hasPart(cur)) {
1410                         //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
1411                         bvcur.setCursor(cur);
1412                         bvcur.setSelection(true);
1413                         //lyxerr << "MOTION " << bvcur << endl;
1414                 } else
1415                         cur.undispatched();
1416         }
1417 }
1418
1419
1420 void InsetMathNest::lfunMouseRelease(Cursor & cur, FuncRequest & cmd)
1421 {
1422         //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
1423
1424         if (cmd.button() == mouse_button::button1) {
1425                 if (!cur.selection())
1426                         cur.noUpdate();
1427                 else {
1428                         Cursor & bvcur = cur.bv().cursor();
1429                         bvcur.setSelection(true);
1430                 }
1431                 return;
1432         }
1433
1434         cur.undispatched();
1435 }
1436
1437
1438 bool InsetMathNest::interpretChar(Cursor & cur, char_type const c)
1439 {
1440         //lyxerr << "interpret 2: '" << c << "'" << endl;
1441         docstring save_selection;
1442         if (c == '^' || c == '_')
1443                 save_selection = grabAndEraseSelection(cur);
1444
1445         cur.clearTargetX();
1446
1447         // handle macroMode
1448         if (cur.inMacroMode()) {
1449                 docstring name = cur.macroName();
1450
1451                 /// are we currently typing '#1' or '#2' or...?
1452                 if (name == "\\#") {
1453                         cur.backspace();
1454                         int n = c - '0';
1455                         if (n >= 1 && n <= 9)
1456                                 cur.insert(new MathMacroArgument(n));
1457                         return true;
1458                 }
1459
1460                 // do not finish macro for known * commands
1461                 MathWordList const & mwl = mathedWordList();
1462                 bool star_macro = c == '*'
1463                         && (mwl.find(name.substr(1) + "*") != mwl.end()
1464                             || cur.buffer()->getMacro(name.substr(1) + "*", cur, true));
1465                 if (isAlphaASCII(c) || star_macro) {
1466                         cur.activeMacro()->setName(name + docstring(1, c));
1467                         return true;
1468                 }
1469
1470                 // handle 'special char' macros
1471                 if (name == "\\") {
1472                         // remove the '\\'
1473                         if (c == '\\') {
1474                                 cur.backspace();
1475                                 if (currentMode() <= InsetMath::TEXT_MODE)
1476                                         cur.niceInsert(createInsetMath("textbackslash"));
1477                                 else
1478                                         cur.niceInsert(createInsetMath("backslash"));
1479                         } else if (c == '^' && currentMode() == InsetMath::MATH_MODE) {
1480                                 cur.backspace();
1481                                 cur.niceInsert(createInsetMath("mathcircumflex"));
1482                         } else if (c == '{') {
1483                                 cur.backspace();
1484                                 cur.niceInsert(MathAtom(new InsetMathBrace));
1485                         } else if (c == '%') {
1486                                 cur.backspace();
1487                                 cur.niceInsert(MathAtom(new InsetMathComment));
1488                         } else if (c == '#') {
1489                                 LASSERT(cur.activeMacro(), /**/);
1490                                 cur.activeMacro()->setName(name + docstring(1, c));
1491                         } else {
1492                                 cur.backspace();
1493                                 cur.niceInsert(createInsetMath(docstring(1, c)));
1494                         }
1495                         return true;
1496                 }
1497
1498                 // One character big delimiters. The others are handled in
1499                 // interpretString().
1500                 latexkeys const * l = in_word_set(name.substr(1));
1501                 if (name[0] == '\\' && l && l->inset == "big") {
1502                         docstring delim;
1503                         switch (c) {
1504                         case '{':
1505                                 delim = from_ascii("\\{");
1506                                 break;
1507                         case '}':
1508                                 delim = from_ascii("\\}");
1509                                 break;
1510                         default:
1511                                 delim = docstring(1, c);
1512                                 break;
1513                         }
1514                         if (InsetMathBig::isBigInsetDelim(delim)) {
1515                                 // name + delim ared a valid InsetMathBig.
1516                                 // We can't use cur.macroModeClose() because
1517                                 // it does not handle delim.
1518                                 InsetMathUnknown * p = cur.activeMacro();
1519                                 p->finalize();
1520                                 --cur.pos();
1521                                 cur.cell().erase(cur.pos());
1522                                 cur.plainInsert(MathAtom(
1523                                         new InsetMathBig(name.substr(1), delim)));
1524                                 return true;
1525                         }
1526                 }
1527
1528                 // leave macro mode and try again if necessary
1529                 if (cur.macroModeClose()) {
1530                         MathAtom const atom = cur.prevAtom();
1531                         if (atom->asNestInset() && atom->isActive()) {
1532                                 cur.posBackward();
1533                                 cur.pushBackward(*cur.nextInset());
1534                         }
1535                 }
1536                 if (c == '{')
1537                         cur.niceInsert(MathAtom(new InsetMathBrace));
1538                 else if (c != ' ')
1539                         interpretChar(cur, c);
1540                 return true;
1541         }
1542
1543
1544         // leave autocorrect mode if necessary
1545         if (lyxrc.autocorrection_math && c == ' ' && cur.autocorrect()) {
1546                 cur.autocorrect() = false;
1547                 cur.message(_("Autocorrect Off ('!' to enter)"));
1548                 return true;
1549         } 
1550         if (lyxrc.autocorrection_math && c == '!' && !cur.autocorrect()) {
1551                 cur.autocorrect() = true;
1552                 cur.message(_("Autocorrect On (<space> to exit)"));
1553                 return true;
1554         }
1555
1556         // just clear selection on pressing the space bar
1557         if (cur.selection() && c == ' ') {
1558                 cur.setSelection(false);
1559                 return true;
1560         }
1561
1562         if (c == '\\') {
1563                 //lyxerr << "starting with macro" << endl;
1564                 bool reduced = cap::reduceSelectionToOneCell(cur);
1565                 if (reduced || !cur.selection()) {
1566                         docstring const safe = cap::grabAndEraseSelection(cur);
1567                         cur.insert(MathAtom(new InsetMathUnknown(from_ascii("\\"), safe, false)));
1568                 }
1569                 return true;
1570         }
1571
1572         selClearOrDel(cur);
1573
1574         MathMacro const * macro = cur.inset().asInsetMath()->asMacro();
1575         if (macro && macro->displayMode() == MathMacro::DISPLAY_UNFOLDED) {
1576                 // resume macro_mode
1577                 docstring const & s = macro->name();
1578                 cur.leaveInset(*macro);
1579                 cur.plainErase();
1580                 docstring safe = grabAndEraseSelection(cur);
1581                 cur.insert(MathAtom(new InsetMathUnknown("\\" + s + c, safe, false)));
1582                 return true;    
1583         }
1584
1585         if (c == '\n') {
1586                 if (currentMode() <= InsetMath::TEXT_MODE)
1587                         cur.insert(c);
1588                 return true;
1589         }
1590
1591         if (c == ' ') {
1592                 if (currentMode() <= InsetMath::TEXT_MODE) {
1593                         // insert spaces in text or undecided mode,
1594                         // but suppress direct insertion of two spaces in a row
1595                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1596                         // it is better than nothing...
1597                         if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ') {
1598                                 cur.insert(c);
1599                                 // FIXME: we have to enable full redraw here because of the
1600                                 // visual box corners that define the inset. If we know for
1601                                 // sure that we stay within the same cell we can optimize for
1602                                 // that using:
1603                                 //cur.updateFlags(Update::SinglePar | Update::FitCursor);
1604                         }
1605                         return true;
1606                 }
1607                 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1608                         cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1609                         // FIXME: we have to enable full redraw here because of the
1610                         // visual box corners that define the inset. If we know for
1611                         // sure that we stay within the same cell we can optimize for
1612                         // that using:
1613                         //cur.updateFlags(Update::SinglePar | Update::FitCursor);
1614                         return true;
1615                 }
1616
1617                 if (cur.popForward()) {
1618                         // FIXME: we have to enable full redraw here because of the
1619                         // visual box corners that define the inset. If we know for
1620                         // sure that we stay within the same cell we can optimize for
1621                         // that using:
1622                         //cur.updateFlags(Update::FitCursor);
1623                         return true;
1624                 }
1625
1626                 // if we are at the very end, leave the formula
1627                 return cur.pos() != cur.lastpos();
1628         }
1629
1630         // These should be treated differently when not in text mode:
1631         if (currentMode() != InsetMath::TEXT_MODE) {
1632                 if (c == '_') {
1633                         script(cur, false, save_selection);
1634                         return true;
1635                 }
1636                 if (c == '^') {
1637                         script(cur, true, save_selection);
1638                         return true;
1639                 }
1640                 if (c == '~') {
1641                         cur.niceInsert(createInsetMath("sim"));
1642                         return true;
1643                 }
1644                 if (currentMode() == InsetMath::MATH_MODE && !isAsciiOrMathAlpha(c)) {
1645                         MathAtom at = createInsetMath("text");
1646                         at.nucleus()->cell(0).push_back(MathAtom(new InsetMathChar(c)));
1647                         cur.niceInsert(at);
1648                         cur.posForward();
1649                         return true;
1650                 }
1651         } else {
1652                 if (c == '^') {
1653                         cur.niceInsert(createInsetMath("textasciicircum"));
1654                         return true;
1655                 }
1656                 if (c == '~') {
1657                         cur.niceInsert(createInsetMath("textasciitilde"));
1658                         return true;
1659                 }
1660         }
1661
1662         if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' ||
1663             c == '%' || c == '_') {
1664                 cur.niceInsert(createInsetMath(docstring(1, c)));
1665                 return true;
1666         }
1667
1668
1669         // try auto-correction
1670         if (lyxrc.autocorrection_math && cur.autocorrect() && cur.pos() != 0
1671                   && math_autocorrect(cur.prevAtom(), c))
1672                 return true;
1673
1674         // no special circumstances, so insert the character without any fuss
1675         cur.insert(c);
1676         if (lyxrc.autocorrection_math) {
1677                 if (!cur.autocorrect())
1678                         cur.message(_("Autocorrect Off ('!' to enter)"));
1679                 else
1680                         cur.message(_("Autocorrect On (<space> to exit)"));
1681         }
1682         return true;
1683 }
1684
1685
1686 bool InsetMathNest::interpretString(Cursor & cur, docstring const & str)
1687 {
1688         // Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
1689         // possible
1690         if (!cur.empty() && cur.pos() > 0 &&
1691             cur.cell()[cur.pos() - 1]->asUnknownInset()) {
1692                 if (InsetMathBig::isBigInsetDelim(str)) {
1693                         docstring prev = asString(cur.cell()[cur.pos() - 1]);
1694                         if (prev[0] == '\\') {
1695                                 prev = prev.substr(1);
1696                                 latexkeys const * l = in_word_set(prev);
1697                                 if (l && l->inset == "big") {
1698                                         cur.cell()[cur.pos() - 1] =
1699                                                 MathAtom(new InsetMathBig(prev, str));
1700                                         return true;
1701                                 }
1702                         }
1703                 }
1704         }
1705         return false;
1706 }
1707
1708
1709 bool InsetMathNest::script(Cursor & cur, bool up)
1710 {
1711         return script(cur, up, docstring());
1712 }
1713
1714
1715 bool InsetMathNest::script(Cursor & cur, bool up,
1716                 docstring const & save_selection)
1717 {
1718         // Hack to get \^ and \_ working
1719         //lyxerr << "handling script: up: " << up << endl;
1720         if (cur.inMacroMode() && cur.macroName() == "\\") {
1721                 if (up)
1722                         cur.niceInsert(createInsetMath("mathcircumflex"));
1723                 else
1724                         interpretChar(cur, '_');
1725                 return true;
1726         }
1727
1728         cur.macroModeClose();
1729         if (asScriptInset() && cur.idx() == 0) {
1730                 // we are in a nucleus of a script inset, move to _our_ script
1731                 InsetMathScript * inset = asScriptInset();
1732                 //lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
1733                 inset->ensure(up);
1734                 cur.idx() = inset->idxOfScript(up);
1735                 cur.pos() = 0;
1736         } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1737                 --cur.pos();
1738                 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
1739                 cur.push(*inset);
1740                 inset->ensure(up);
1741                 cur.idx() = inset->idxOfScript(up);
1742                 cur.pos() = cur.lastpos();
1743         } else {
1744                 // convert the thing to our left to a scriptinset or create a new
1745                 // one if in the very first position of the array
1746                 if (cur.pos() == 0) {
1747                         //lyxerr << "new scriptinset" << endl;
1748                         cur.insert(new InsetMathScript(up));
1749                 } else {
1750                         //lyxerr << "converting prev atom " << endl;
1751                         cur.prevAtom() = MathAtom(new InsetMathScript(cur.prevAtom(), up));
1752                 }
1753                 --cur.pos();
1754                 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
1755                 // See comment in MathParser.cpp for special handling of {}-bases
1756
1757                 cur.push(*inset);
1758                 cur.idx() = 1;
1759                 cur.pos() = 0;
1760         }
1761         //lyxerr << "inserting selection 1:\n" << save_selection << endl;
1762         cur.niceInsert(save_selection);
1763         cur.resetAnchor();
1764         //lyxerr << "inserting selection 2:\n" << save_selection << endl;
1765         return true;
1766 }
1767
1768
1769 bool InsetMathNest::completionSupported(Cursor const & cur) const
1770 {
1771         return cur.inMacroMode();
1772 }
1773
1774
1775 bool InsetMathNest::inlineCompletionSupported(Cursor const & cur) const
1776 {
1777         return cur.inMacroMode();
1778 }
1779
1780
1781 bool InsetMathNest::automaticInlineCompletion() const
1782 {
1783         return lyxrc.completion_inline_math;
1784 }
1785
1786
1787 bool InsetMathNest::automaticPopupCompletion() const
1788 {
1789         return lyxrc.completion_popup_math;
1790 }
1791
1792
1793 CompletionList const *
1794 InsetMathNest::createCompletionList(Cursor const & cur) const
1795 {
1796         if (!cur.inMacroMode())
1797                 return 0;
1798
1799         return new MathCompletionList(cur);
1800 }
1801
1802
1803 docstring InsetMathNest::completionPrefix(Cursor const & cur) const
1804 {
1805         if (!cur.inMacroMode())
1806                 return docstring();
1807
1808         return cur.activeMacro()->name();
1809 }
1810
1811
1812 bool InsetMathNest::insertCompletion(Cursor & cur, docstring const & s,
1813                                      bool finished)
1814 {
1815         if (!cur.inMacroMode())
1816                 return false;
1817
1818         // append completion to active macro
1819         InsetMathUnknown * inset = cur.activeMacro();
1820         inset->setName(inset->name() + s);
1821
1822         // finish macro
1823         if (finished) {
1824 #if 0
1825                 // FIXME: this creates duplicates in the completion popup
1826                 // which looks ugly. Moreover the changes the list lengths
1827                 // which seems to
1828                 confuse the popup as well.
1829                 MathCompletionList::addToFavorites(inset->name());
1830 #endif
1831                 lyx::dispatch(FuncRequest(LFUN_SELF_INSERT, " "));
1832         }
1833
1834         return true;
1835 }
1836
1837
1838 void InsetMathNest::completionPosAndDim(Cursor const & cur, int & x, int & y,
1839                                         Dimension & dim) const
1840 {
1841         Inset const * inset = cur.activeMacro();
1842         if (!inset)
1843                 return;
1844
1845         // get inset dimensions
1846         dim = cur.bv().coordCache().insets().dim(inset);
1847         // FIXME: these 3 are no accurate, but should depend on the font.
1848         // Now the popup jumps down if you enter a char with descent > 0.
1849         dim.des += 3;
1850         dim.asc += 3;
1851
1852         // and position
1853         Point xy
1854         = cur.bv().coordCache().insets().xy(inset);
1855         x = xy.x_;
1856         y = xy.y_;
1857 }
1858
1859
1860 bool InsetMathNest::cursorMathForward(Cursor & cur)
1861 {
1862         if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
1863                 cur.pushBackward(*cur.nextAtom().nucleus());
1864                 cur.inset().idxFirst(cur);
1865                 return true;
1866         }
1867         if (cur.posForward() || idxForward(cur))
1868                 return true;
1869         // try to pop forwards --- but don't pop out of math! leave that to
1870         // the FINISH lfuns
1871         int s = cur.depth() - 2;
1872         if (s >= 0 && cur[s].inset().asInsetMath())
1873                 return cur.popForward();
1874         return false;
1875 }
1876
1877
1878 bool InsetMathNest::cursorMathBackward(Cursor & cur)
1879 {
1880         if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
1881                 cur.posBackward();
1882                 cur.push(*cur.nextAtom().nucleus());
1883                 cur.inset().idxLast(cur);
1884                 return true;
1885         }
1886         if (cur.posBackward() || idxBackward(cur))
1887                 return true;
1888         // try to pop backwards --- but don't pop out of math! leave that to
1889         // the FINISH lfuns
1890         int s = cur.depth() - 2;
1891         if (s >= 0 && cur[s].inset().asInsetMath())
1892                 return cur.popBackward();
1893         return false;
1894 }
1895
1896
1897 ////////////////////////////////////////////////////////////////////
1898
1899 MathCompletionList::MathCompletionList(Cursor const & cur)
1900 {
1901         // fill it with macros from the buffer
1902         MacroNameSet macros;
1903         cur.buffer()->listMacroNames(macros);
1904         MacroNameSet::const_iterator it;
1905         for (it = macros.begin(); it != macros.end(); ++it) {
1906                 if (cur.buffer()->getMacro(*it, cur, false))
1907                         locals.push_back("\\" + *it);
1908         }
1909         sort(locals.begin(), locals.end());
1910
1911         if (globals.size() > 0)
1912                 return;
1913
1914         // fill in global macros
1915         macros.clear();
1916         MacroTable::globalMacros().getMacroNames(macros);
1917         //lyxerr << "Globals completion macros: ";
1918         for (it = macros.begin(); it != macros.end(); ++it) {
1919                 //lyxerr << "\\" + *it << " ";
1920                 globals.push_back("\\" + *it);
1921         }
1922         //lyxerr << std::endl;
1923
1924         // fill in global commands
1925         globals.push_back(from_ascii("\\boxed"));
1926         globals.push_back(from_ascii("\\fbox"));
1927         globals.push_back(from_ascii("\\framebox"));
1928         globals.push_back(from_ascii("\\makebox"));
1929         globals.push_back(from_ascii("\\kern"));
1930         globals.push_back(from_ascii("\\xrightarrow"));
1931         globals.push_back(from_ascii("\\xleftarrow"));
1932         globals.push_back(from_ascii("\\split"));
1933         globals.push_back(from_ascii("\\gathered"));
1934         globals.push_back(from_ascii("\\aligned"));
1935         globals.push_back(from_ascii("\\alignedat"));
1936         globals.push_back(from_ascii("\\cases"));
1937         globals.push_back(from_ascii("\\substack"));
1938         globals.push_back(from_ascii("\\xymatrix"));
1939         globals.push_back(from_ascii("\\subarray"));
1940         globals.push_back(from_ascii("\\array"));
1941         globals.push_back(from_ascii("\\sqrt"));
1942         globals.push_back(from_ascii("\\root"));
1943         globals.push_back(from_ascii("\\tabular"));
1944         globals.push_back(from_ascii("\\stackrel"));
1945         globals.push_back(from_ascii("\\binom"));
1946         globals.push_back(from_ascii("\\choose"));
1947         globals.push_back(from_ascii("\\brace"));
1948         globals.push_back(from_ascii("\\brack"));
1949         globals.push_back(from_ascii("\\frac"));
1950         globals.push_back(from_ascii("\\over"));
1951         globals.push_back(from_ascii("\\nicefrac"));
1952         globals.push_back(from_ascii("\\unitfrac"));
1953         globals.push_back(from_ascii("\\unitfracthree"));
1954         globals.push_back(from_ascii("\\unitone"));
1955         globals.push_back(from_ascii("\\unittwo"));
1956         globals.push_back(from_ascii("\\infer"));
1957         globals.push_back(from_ascii("\\atop"));
1958         globals.push_back(from_ascii("\\lefteqn"));
1959         globals.push_back(from_ascii("\\boldsymbol"));
1960         globals.push_back(from_ascii("\\bm"));
1961         globals.push_back(from_ascii("\\color"));
1962         globals.push_back(from_ascii("\\normalcolor"));
1963         globals.push_back(from_ascii("\\textcolor"));
1964         globals.push_back(from_ascii("\\cfrac"));
1965         globals.push_back(from_ascii("\\cfracleft"));
1966         globals.push_back(from_ascii("\\cfracright"));
1967         globals.push_back(from_ascii("\\dfrac"));
1968         globals.push_back(from_ascii("\\tfrac"));
1969         globals.push_back(from_ascii("\\dbinom"));
1970         globals.push_back(from_ascii("\\tbinom"));
1971         globals.push_back(from_ascii("\\hphantom"));
1972         globals.push_back(from_ascii("\\phantom"));
1973         globals.push_back(from_ascii("\\vphantom"));
1974         MathWordList const & words = mathedWordList();
1975         MathWordList::const_iterator it2;
1976         //lyxerr << "Globals completion commands: ";
1977         for (it2 = words.begin(); it2 != words.end(); ++it2) {
1978                 globals.push_back("\\" + (*it2).first);
1979                 //lyxerr << "\\" + (*it2).first << " ";
1980         }
1981         //lyxerr << std::endl;
1982         sort(globals.begin(), globals.end());
1983 }
1984
1985
1986 MathCompletionList::~MathCompletionList()
1987 {
1988 }
1989
1990
1991 size_type MathCompletionList::size() const
1992 {
1993         return locals.size() + globals.size();
1994 }
1995
1996
1997 docstring const & MathCompletionList::data(size_t idx) const
1998 {
1999         size_t lsize = locals.size();
2000         if (idx >= lsize)
2001                 return globals[idx - lsize];
2002         else
2003                 return locals[idx];
2004 }
2005
2006
2007 std::string MathCompletionList::icon(size_t idx) const
2008 {
2009         // get the latex command
2010         docstring cmd;
2011         size_t lsize = locals.size();
2012         if (idx >= lsize)
2013                 cmd = globals[idx - lsize];
2014         else
2015                 cmd = locals[idx];
2016
2017         // get the icon resource name by stripping the backslash
2018         return "images/math/" + to_utf8(cmd.substr(1)) + ".png";
2019 }
2020
2021 std::vector<docstring> MathCompletionList::globals;
2022
2023 } // namespace lyx