]> git.lyx.org Git - features.git/blob - src/mathed/InsetMathNest.cpp
Put MathData on a diet: transfer dimension cache to BufferView' CoordCache along...
[features.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 "InsetMathColor.h"
20 #include "InsetMathComment.h"
21 #include "InsetMathDelim.h"
22 #include "InsetMathHull.h"
23 //#include "InsetMathMBox.h"
24 #include "InsetMathRef.h"
25 #include "InsetMathScript.h"
26 #include "InsetMathSpace.h"
27 #include "InsetMathSymbol.h"
28 #include "InsetMathUnknown.h"
29 #include "MathData.h"
30 #include "MathFactory.h"
31 #include "MathMacroArgument.h"
32 #include "MathParser.h"
33 #include "MathStream.h"
34 #include "MathSupport.h"
35
36 #include "Bidi.h"
37 #include "bufferview_funcs.h"
38 #include "BufferView.h"
39 #include "Color.h"
40 #include "CoordCache.h"
41 #include "Cursor.h"
42 #include "CutAndPaste.h"
43 #include "debug.h"
44 #include "DispatchResult.h"
45 #include "FuncRequest.h"
46 #include "FuncStatus.h"
47 #include "gettext.h"
48 #include "Text.h"
49 #include "OutputParams.h"
50 #include "Undo.h"
51
52 #include "support/lstrings.h"
53 #include "support/textutils.h"
54
55 #include "frontends/Clipboard.h"
56 #include "frontends/Painter.h"
57 #include "frontends/Selection.h"
58
59 #include "FuncRequest.h"
60
61 #include <sstream>
62
63
64 namespace lyx {
65
66 using cap::copySelection;
67 using cap::grabAndEraseSelection;
68 using cap::cutSelection;
69 using cap::replaceSelection;
70 using cap::selClearOrDel;
71
72 using std::endl;
73 using std::string;
74 using std::istringstream;
75
76
77 InsetMathNest::InsetMathNest(idx_type nargs)
78         : cells_(nargs), lock_(false), mouse_hover_(false)
79 {}
80
81
82 InsetMathNest::InsetMathNest(InsetMathNest const & inset)
83         : InsetMath(inset), cells_(inset.cells_), lock_(inset.lock_),
84           mouse_hover_(false)
85 {}
86
87
88 InsetMathNest & InsetMathNest::operator=(InsetMathNest const & inset)
89 {
90         cells_ = inset.cells_;
91         lock_ = inset.lock_;
92         mouse_hover_ = false;
93         InsetMath::operator=(inset);
94         return *this;
95 }
96
97
98 InsetMath::idx_type InsetMathNest::nargs() const
99 {
100         return cells_.size();
101 }
102
103
104 void InsetMathNest::cursorPos(BufferView const & bv,
105                 CursorSlice const & sl, bool /*boundary*/,
106                 int & x, int & y) const
107 {
108 // FIXME: This is a hack. Ideally, the coord cache should not store
109 // absolute positions, but relative ones. This would mean to call
110 // setXY() not in MathData::draw(), but in the parent insets' draw()
111 // with the correctly adjusted x,y values. But this means that we'd have
112 // to touch all (math)inset's draw() methods. Right now, we'll store
113 // absolute value, and make them here relative, only to make them
114 // absolute again when actually drawing the cursor. What a mess.
115         BOOST_ASSERT(ptr_cmp(&sl.inset(), this));
116         MathData const & ar = sl.cell();
117         CoordCache const & coord_cache = bv.coordCache();
118         if (!coord_cache.getArrays().has(&ar)) {
119                 // this can (semi-)legally happen if we just created this cell
120                 // and it never has been drawn before. So don't ASSERT.
121                 //lyxerr << "no cached data for array " << &ar << endl;
122                 x = 0;
123                 y = 0;
124                 return;
125         }
126         Point const pt = coord_cache.getArrays().xy(&ar);
127         if (!coord_cache.getInsets().has(this)) {
128                 // same as above
129                 //lyxerr << "no cached data for inset " << this << endl;
130                 x = 0;
131                 y = 0;
132                 return;
133         }
134         Point const pt2 = coord_cache.getInsets().xy(this);
135         //lyxerr << "retrieving position cache for MathData "
136         //      << pt.x_ << ' ' << pt.y_ << std::endl;
137         x = pt.x_ - pt2.x_ + ar.pos2x(sl.pos());
138         y = pt.y_ - pt2.y_;
139 //      lyxerr << "pt.y_ : " << pt.y_ << " pt2_.y_ : " << pt2.y_
140 //              << " asc: " << ascent() << "  des: " << descent()
141 //              << " ar.asc: " << ar.ascent() << " ar.des: " << ar.descent() << endl;
142         // move cursor visually into empty cells ("blue rectangles");
143         if (ar.empty())
144                 x += 2;
145 }
146
147
148 void InsetMathNest::metrics(MetricsInfo const & mi) const
149 {
150         MetricsInfo m = mi;
151         for (idx_type i = 0, n = nargs(); i != n; ++i) {
152                 Dimension dim;
153                 cell(i).metrics(m, dim);
154         }
155 }
156
157
158 bool InsetMathNest::idxNext(Cursor & cur) const
159 {
160         BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
161         if (cur.idx() == cur.lastidx())
162                 return false;
163         ++cur.idx();
164         cur.pos() = 0;
165         return true;
166 }
167
168
169 bool InsetMathNest::idxRight(Cursor & cur) const
170 {
171         return idxNext(cur);
172 }
173
174
175 bool InsetMathNest::idxPrev(Cursor & cur) const
176 {
177         BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
178         if (cur.idx() == 0)
179                 return false;
180         --cur.idx();
181         cur.pos() = cur.lastpos();
182         return true;
183 }
184
185
186 bool InsetMathNest::idxLeft(Cursor & cur) const
187 {
188         return idxPrev(cur);
189 }
190
191
192 bool InsetMathNest::idxFirst(Cursor & cur) const
193 {
194         BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
195         if (nargs() == 0)
196                 return false;
197         cur.idx() = 0;
198         cur.pos() = 0;
199         return true;
200 }
201
202
203 bool InsetMathNest::idxLast(Cursor & cur) const
204 {
205         BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
206         if (nargs() == 0)
207                 return false;
208         cur.idx() = cur.lastidx();
209         cur.pos() = cur.lastpos();
210         return true;
211 }
212
213
214 void InsetMathNest::dump() const
215 {
216         odocstringstream oss;
217         WriteStream os(oss);
218         os << "---------------------------------------------\n";
219         write(os);
220         os << "\n";
221         for (idx_type i = 0, n = nargs(); i != n; ++i)
222                 os << cell(i) << "\n";
223         os << "---------------------------------------------\n";
224         lyxerr << to_utf8(oss.str());
225 }
226
227
228 void InsetMathNest::draw(PainterInfo & pi, int x, int y) const
229 {
230 #if 0
231         if (lock_)
232                 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
233                                         Color::mathlockbg);
234 #endif
235         setPosCache(pi, x, y);
236 }
237
238
239 void InsetMathNest::drawSelection(PainterInfo & pi, int x, int y) const
240 {
241         BufferView & bv = *pi.base.bv;
242         // this should use the x/y values given, not the cached values
243         Cursor & cur = bv.cursor();
244         if (!cur.selection())
245                 return;
246         if (!ptr_cmp(&cur.inset(), this))
247                 return;
248
249         // FIXME: hack to get position cache warm
250         pi.pain.setDrawingEnabled(false);
251         draw(pi, x, y);
252         pi.pain.setDrawingEnabled(true);
253
254         CursorSlice s1 = cur.selBegin();
255         CursorSlice s2 = cur.selEnd();
256
257         //lyxerr << "InsetMathNest::drawing selection: "
258         //      << " s1: " << s1 << " s2: " << s2 << endl;
259         if (s1.idx() == s2.idx()) {
260                 MathData const & c = cell(s1.idx());
261                 Geometry const & g = bv.coordCache().getArrays().geometry(&c);
262                 int x1 = g.pos.x_ + c.pos2x(s1.pos());
263                 int y1 = g.pos.y_ - g.dim.ascent();
264                 int x2 = g.pos.x_ + c.pos2x(s2.pos());
265                 int y2 = g.pos.y_ + g.dim.descent();
266                 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, Color::selection);
267         //lyxerr << "InsetMathNest::drawing selection 3: "
268         //      << " x1: " << x1 << " x2: " << x2
269         //      << " y1: " << y1 << " y2: " << y2 << endl;
270         } else {
271                 for (idx_type i = 0; i < nargs(); ++i) {
272                         if (idxBetween(i, s1.idx(), s2.idx())) {
273                                 MathData const & c = cell(i);
274                                 Geometry const & g = bv.coordCache().getArrays().geometry(&c);
275                                 int x1 = g.pos.x_;
276                                 int y1 = g.pos.y_ - g.dim.ascent();
277                                 int x2 = g.pos.x_ + g.dim.width();
278                                 int y2 = g.pos.y_ + g.dim.descent();
279                                 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, Color::selection);
280                         }
281                 }
282         }
283 }
284
285
286 void InsetMathNest::validate(LaTeXFeatures & features) const
287 {
288         for (idx_type i = 0; i < nargs(); ++i)
289                 cell(i).validate(features);
290 }
291
292
293 void InsetMathNest::replace(ReplaceData & rep)
294 {
295         for (idx_type i = 0; i < nargs(); ++i)
296                 cell(i).replace(rep);
297 }
298
299
300 bool InsetMathNest::contains(MathData const & ar) const
301 {
302         for (idx_type i = 0; i < nargs(); ++i)
303                 if (cell(i).contains(ar))
304                         return true;
305         return false;
306 }
307
308
309 bool InsetMathNest::lock() const
310 {
311         return lock_;
312 }
313
314
315 void InsetMathNest::lock(bool l)
316 {
317         lock_ = l;
318 }
319
320
321 bool InsetMathNest::isActive() const
322 {
323         return nargs() > 0;
324 }
325
326
327 MathData InsetMathNest::glue() const
328 {
329         MathData ar;
330         for (size_t i = 0; i < nargs(); ++i)
331                 ar.append(cell(i));
332         return ar;
333 }
334
335
336 void InsetMathNest::write(WriteStream & os) const
337 {
338         os << '\\' << name().c_str();
339         for (size_t i = 0; i < nargs(); ++i)
340                 os << '{' << cell(i) << '}';
341         if (nargs() == 0)
342                 os.pendingSpace(true);
343         if (lock_ && !os.latex()) {
344                 os << "\\lyxlock";
345                 os.pendingSpace(true);
346         }
347 }
348
349
350 void InsetMathNest::normalize(NormalStream & os) const
351 {
352         os << '[' << name().c_str();
353         for (size_t i = 0; i < nargs(); ++i)
354                 os << ' ' << cell(i);
355         os << ']';
356 }
357
358
359 int InsetMathNest::latex(Buffer const &, odocstream & os,
360                         OutputParams const & runparams) const
361 {
362         WriteStream wi(os, runparams.moving_arg, true);
363         write(wi);
364         return wi.line();
365 }
366
367
368 bool InsetMathNest::setMouseHover(bool mouse_hover)
369 {
370         mouse_hover_ = mouse_hover;
371         return true;
372 }
373
374
375 bool InsetMathNest::notifyCursorLeaves(Cursor & /*cur*/)
376 {
377         // FIXME: look here
378 #if 0
379         MathData & ar = cur.cell();
380         // remove base-only "scripts"
381         for (pos_type i = 0; i + 1 < ar.size(); ++i) {
382                 InsetMathScript * p = operator[](i).nucleus()->asScriptInset();
383                 if (p && p->nargs() == 1) {
384                         MathData ar = p->nuc();
385                         erase(i);
386                         insert(i, ar);
387                         cur.adjust(i, ar.size() - 1);
388                 }
389         }
390
391         // glue adjacent font insets of the same kind
392         for (pos_type i = 0; i + 1 < size(); ++i) {
393                 InsetMathFont * p = operator[](i).nucleus()->asFontInset();
394                 InsetMathFont const * q = operator[](i + 1)->asFontInset();
395                 if (p && q && p->name() == q->name()) {
396                         p->cell(0).append(q->cell(0));
397                         erase(i + 1);
398                         cur.adjust(i, -1);
399                 }
400         }
401 #endif
402         return false;
403 }
404
405
406 void InsetMathNest::handleFont
407         (Cursor & cur, docstring const & arg, char const * const font)
408 {
409         handleFont(cur, arg, from_ascii(font));
410 }
411
412
413 void InsetMathNest::handleFont
414         (Cursor & cur, docstring const & arg, docstring const & font)
415 {
416         // this whole function is a hack and won't work for incremental font
417         // changes...
418
419         if (cur.inset().asInsetMath()->name() == font) {
420                 recordUndoInset(cur, Undo::ATOMIC);
421                 cur.handleFont(to_utf8(font));
422         } else {
423                 recordUndo(cur, Undo::ATOMIC);
424                 cur.handleNest(createInsetMath(font));
425                 cur.insert(arg);
426         }
427 }
428
429
430 void InsetMathNest::handleFont2(Cursor & cur, docstring const & arg)
431 {
432         recordUndo(cur, Undo::ATOMIC);
433         Font font;
434         bool b;
435         bv_funcs::string2font(to_utf8(arg), font, b);
436         if (font.color() != Color::inherit) {
437                 MathAtom at = MathAtom(new InsetMathColor(true, font.color()));
438                 cur.handleNest(at, 0);
439         }
440 }
441
442
443 void InsetMathNest::doDispatch(Cursor & cur, FuncRequest & cmd)
444 {
445         //lyxerr << "InsetMathNest: request: " << cmd << std::endl;
446         //CursorSlice sl = cur.current();
447
448         switch (cmd.action) {
449
450         case LFUN_PASTE: {
451                 recordUndo(cur);
452                 cur.message(_("Paste"));
453                 replaceSelection(cur);
454                 docstring topaste;
455                 if (cmd.argument().empty() && !theClipboard().isInternal())
456                         topaste = theClipboard().getAsText();
457                 else {
458                         size_t n = 0;
459                         idocstringstream is(cmd.argument());
460                         is >> n;
461                         topaste = cap::getSelection(cur.buffer(), n);
462                 }
463                 cur.niceInsert(topaste);
464                 cur.clearSelection(); // bug 393
465                 finishUndo();
466                 break;
467         }
468
469         case LFUN_CUT:
470                 recordUndo(cur);
471                 cutSelection(cur, true, true);
472                 cur.message(_("Cut"));
473                 // Prevent stale position >= size crash
474                 // Probably not necessary anymore, see eraseSelection (gb 2005-10-09)
475                 cur.normalize();
476                 break;
477
478         case LFUN_COPY:
479                 copySelection(cur);
480                 cur.message(_("Copy"));
481                 break;
482
483         case LFUN_MOUSE_PRESS:
484                 lfunMousePress(cur, cmd);
485                 break;
486
487         case LFUN_MOUSE_MOTION:
488                 lfunMouseMotion(cur, cmd);
489                 break;
490
491         case LFUN_MOUSE_RELEASE:
492                 lfunMouseRelease(cur, cmd);
493                 break;
494
495         case LFUN_FINISHED_LEFT:
496                 cur.bv().cursor() = cur;
497                 break;
498
499         case LFUN_FINISHED_RIGHT:
500                 ++cur.pos();
501                 cur.bv().cursor() = cur;
502                 break;
503
504         case LFUN_CHAR_FORWARD:
505                 cur.updateFlags(Update::Decoration | Update::FitCursor);
506         case LFUN_CHAR_FORWARD_SELECT:
507                 cur.selHandle(cmd.action == LFUN_CHAR_FORWARD_SELECT);
508                 cur.autocorrect() = false;
509                 cur.clearTargetX();
510                 cur.macroModeClose();
511                 if (reverseDirectionNeeded(cur))
512                         goto goto_char_backwards;
513
514 goto_char_forwards:
515                 if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
516                         cur.pushLeft(*cur.nextAtom().nucleus());
517                         cur.inset().idxFirst(cur);
518                 } else if (cur.posRight() || idxRight(cur)
519                         || cur.popRight() || cur.selection())
520                         ;
521                 else {
522                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
523                         cur.undispatched();
524                 }
525                 break;
526
527         case LFUN_CHAR_BACKWARD:
528                 cur.updateFlags(Update::Decoration | Update::FitCursor);
529         case LFUN_CHAR_BACKWARD_SELECT:
530                 cur.selHandle(cmd.action == LFUN_CHAR_BACKWARD_SELECT);
531                 cur.autocorrect() = false;
532                 cur.clearTargetX();
533                 cur.macroModeClose();
534                 if (reverseDirectionNeeded(cur))
535                         goto goto_char_forwards;
536
537 goto_char_backwards:
538                 if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
539                         cur.posLeft();
540                         cur.push(*cur.nextAtom().nucleus());
541                         cur.inset().idxLast(cur);
542                 } else if (cur.posLeft() || idxLeft(cur)
543                         || cur.popLeft() || cur.selection())
544                         ;
545                 else {
546                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
547                         cur.undispatched();
548                 }
549                 break;
550
551         case LFUN_DOWN:
552         case LFUN_UP:
553                 cur.updateFlags(Update::Decoration | Update::FitCursor);
554         case LFUN_DOWN_SELECT:
555         case LFUN_UP_SELECT: {
556                 // close active macro
557                 if (cur.inMacroMode()) {
558                         cur.macroModeClose();
559                         break;
560                 }
561                 
562                 // stop/start the selection
563                 bool select = cmd.action == LFUN_DOWN_SELECT ||
564                         cmd.action == LFUN_UP_SELECT;
565                 cur.selHandle(select);
566                 
567                 // go up/down
568                 bool up = cmd.action == LFUN_UP || cmd.action == LFUN_UP_SELECT;
569                 bool successful = cur.upDownInMath(up);
570                 if (successful) {
571                         // notify left insets and give them chance to set update flags
572                         lyx::notifyCursorLeaves(cur.beforeDispatchCursor(), cur);
573                         cur.fixIfBroken();
574                         break;
575                 }
576                 
577                 if (cur.fixIfBroken())
578                         // FIXME: Something bad happened. We pass the corrected Cursor
579                         // instead of letting things go worse.
580                         break;
581
582                 // We did not manage to move the cursor.
583                 cur.undispatched();
584                 break;
585         }
586
587         case LFUN_MOUSE_DOUBLE:
588         case LFUN_MOUSE_TRIPLE:
589         case LFUN_WORD_SELECT:
590                 cur.pos() = 0;
591                 cur.idx() = 0;
592                 cur.resetAnchor();
593                 cur.selection() = true;
594                 cur.pos() = cur.lastpos();
595                 cur.idx() = cur.lastidx();
596                 break;
597
598         case LFUN_PARAGRAPH_UP:
599         case LFUN_PARAGRAPH_DOWN:
600                 cur.updateFlags(Update::Decoration | Update::FitCursor);
601         case LFUN_PARAGRAPH_UP_SELECT:
602         case LFUN_PARAGRAPH_DOWN_SELECT:
603                 break;
604
605         case LFUN_LINE_BEGIN:
606         case LFUN_WORD_BACKWARD:
607                 cur.updateFlags(Update::Decoration | Update::FitCursor);
608         case LFUN_LINE_BEGIN_SELECT:
609         case LFUN_WORD_BACKWARD_SELECT:
610                 cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT ||
611                                 cmd.action == LFUN_LINE_BEGIN_SELECT);
612                 cur.macroModeClose();
613                 if (cur.pos() != 0) {
614                         cur.pos() = 0;
615                 } else if (cur.col() != 0) {
616                         cur.idx() -= cur.col();
617                         cur.pos() = 0;
618                 } else if (cur.idx() != 0) {
619                         cur.idx() = 0;
620                         cur.pos() = 0;
621                 } else {
622                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
623                         cur.undispatched();
624                 }
625                 break;
626
627         case LFUN_WORD_FORWARD:
628         case LFUN_LINE_END:
629                 cur.updateFlags(Update::Decoration | Update::FitCursor);
630         case LFUN_WORD_FORWARD_SELECT:
631         case LFUN_LINE_END_SELECT:
632                 cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT ||
633                                 cmd.action == LFUN_LINE_END_SELECT);
634                 cur.macroModeClose();
635                 cur.clearTargetX();
636                 if (cur.pos() != cur.lastpos()) {
637                         cur.pos() = cur.lastpos();
638                 } else if (ncols() && (cur.col() != cur.lastcol())) {
639                         cur.idx() = cur.idx() - cur.col() + cur.lastcol();
640                         cur.pos() = cur.lastpos();
641                 } else if (cur.idx() != cur.lastidx()) {
642                         cur.idx() = cur.lastidx();
643                         cur.pos() = cur.lastpos();
644                 } else {
645                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
646                         cur.undispatched();
647                 }
648                 break;
649
650         case LFUN_SCREEN_UP_SELECT:
651                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
652                 cur.undispatched();
653                 break;
654
655         case LFUN_SCREEN_DOWN_SELECT:
656                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
657                 cur.undispatched();
658                 break;
659
660         case LFUN_CELL_FORWARD:
661                 cur.updateFlags(Update::Decoration | Update::FitCursor);
662                 cur.inset().idxNext(cur);
663                 break;
664
665         case LFUN_CELL_BACKWARD:
666                 cur.updateFlags(Update::Decoration | Update::FitCursor);
667                 cur.inset().idxPrev(cur);
668                 break;
669
670         case LFUN_WORD_DELETE_BACKWARD:
671         case LFUN_CHAR_DELETE_BACKWARD:
672                 if (cur.pos() == 0)
673                         // May affect external cell:
674                         recordUndoInset(cur, Undo::ATOMIC);
675                 else
676                         recordUndo(cur, Undo::ATOMIC);
677                 // if the inset can not be removed from within, delete it
678                 if (!cur.backspace()) {
679                         FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
680                         cur.innerText()->dispatch(cur, cmd);
681                 }
682                 break;
683
684         case LFUN_WORD_DELETE_FORWARD:
685         case LFUN_CHAR_DELETE_FORWARD:
686                 if (cur.pos() == cur.lastpos())
687                         // May affect external cell:
688                         recordUndoInset(cur, Undo::ATOMIC);
689                 else
690                         recordUndo(cur, Undo::ATOMIC);
691                 // if the inset can not be removed from within, delete it
692                 if (!cur.erase()) {
693                         FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
694                         cur.innerText()->dispatch(cur, cmd);
695                 }
696                 break;
697
698         case LFUN_ESCAPE:
699                 if (cur.selection())
700                         cur.clearSelection();
701                 else  {
702                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
703                         cur.undispatched();
704                 }
705                 break;
706
707         case LFUN_INSET_TOGGLE:
708                 recordUndo(cur);
709                 lock(!lock());
710                 cur.popRight();
711                 break;
712
713         case LFUN_SELF_INSERT:
714                 if (cmd.argument().size() != 1) {
715                         recordUndo(cur);
716                         docstring const arg = cmd.argument();
717                         if (!interpretString(cur, arg))
718                                 cur.insert(arg);
719                         break;
720                 }
721                 // Don't record undo steps if we are in macro mode and
722                 // cmd.argument is the next character of the macro name.
723                 // Otherwise we'll get an invalid cursor if we undo after
724                 // the macro was finished and the macro is a known command,
725                 // e.g. sqrt. Cursor::macroModeClose replaces in this case
726                 // the InsetMathUnknown with name "frac" by an empty
727                 // InsetMathFrac -> a pos value > 0 is invalid.
728                 // A side effect is that an undo before the macro is finished
729                 // undoes the complete macro, not only the last character.
730                 if (!cur.inMacroMode())
731                         recordUndo(cur);
732
733                 // spacial handling of space. If we insert an inset
734                 // via macro mode, we want to put the cursor inside it
735                 // if relevant. Think typing "\frac<space>".
736                 if (cmd.argument()[0] == ' '
737                     && cur.inMacroMode() && cur.macroName() != "\\"
738                     && cur.macroModeClose()) {
739                         MathAtom const atom = cur.prevAtom();
740                         if (atom->asNestInset() && atom->isActive()) {
741                                 cur.posLeft();
742                                 cur.pushLeft(*cur.nextInset());
743                         }
744                 } else if (!interpretChar(cur, cmd.argument()[0])) {
745                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
746                         cur.undispatched();
747                 }
748                 break;
749
750         //case LFUN_SERVER_GET_XY:
751         //      sprintf(dispatch_buffer, "%d %d",);
752         //      break;
753
754         case LFUN_SERVER_SET_XY: {
755                 lyxerr << "LFUN_SERVER_SET_XY broken!" << endl;
756                 int x = 0;
757                 int y = 0;
758                 istringstream is(to_utf8(cmd.argument()));
759                 is >> x >> y;
760                 cur.setScreenPos(x, y);
761                 break;
762         }
763
764         // Special casing for superscript in case of LyX handling
765         // dead-keys:
766         case LFUN_ACCENT_CIRCUMFLEX:
767                 if (cmd.argument().empty()) {
768                         // do superscript if LyX handles
769                         // deadkeys
770                         recordUndo(cur, Undo::ATOMIC);
771                         script(cur, true, grabAndEraseSelection(cur));
772                 }
773                 break;
774
775         case LFUN_ACCENT_UMLAUT:
776         case LFUN_ACCENT_ACUTE:
777         case LFUN_ACCENT_GRAVE:
778         case LFUN_ACCENT_BREVE:
779         case LFUN_ACCENT_DOT:
780         case LFUN_ACCENT_MACRON:
781         case LFUN_ACCENT_CARON:
782         case LFUN_ACCENT_TILDE:
783         case LFUN_ACCENT_CEDILLA:
784         case LFUN_ACCENT_CIRCLE:
785         case LFUN_ACCENT_UNDERDOT:
786         case LFUN_ACCENT_TIE:
787         case LFUN_ACCENT_OGONEK:
788         case LFUN_ACCENT_HUNGARIAN_UMLAUT:
789                 break;
790
791         //  Math fonts
792         case LFUN_FONT_FREE_APPLY:
793         case LFUN_FONT_FREE_UPDATE:
794                 handleFont2(cur, cmd.argument());
795                 break;
796
797         case LFUN_FONT_BOLD:
798                 if (currentMode() == TEXT_MODE)
799                         handleFont(cur, cmd.argument(), "textbf");
800                 else
801                         handleFont(cur, cmd.argument(), "mathbf");
802                 break;
803         case LFUN_FONT_SANS:
804                 if (currentMode() == TEXT_MODE)
805                         handleFont(cur, cmd.argument(), "textsf");
806                 else
807                         handleFont(cur, cmd.argument(), "mathsf");
808                 break;
809         case LFUN_FONT_EMPH:
810                 if (currentMode() == TEXT_MODE)
811                         handleFont(cur, cmd.argument(), "emph");
812                 else
813                         handleFont(cur, cmd.argument(), "mathcal");
814                 break;
815         case LFUN_FONT_ROMAN:
816                 if (currentMode() == TEXT_MODE)
817                         handleFont(cur, cmd.argument(), "textrm");
818                 else
819                         handleFont(cur, cmd.argument(), "mathrm");
820                 break;
821         case LFUN_FONT_TYPEWRITER:
822                 if (currentMode() == TEXT_MODE)
823                         handleFont(cur, cmd.argument(), "texttt");
824                 else
825                         handleFont(cur, cmd.argument(), "mathtt");
826                 break;
827         case LFUN_FONT_FRAK:
828                 handleFont(cur, cmd.argument(), "mathfrak");
829                 break;
830         case LFUN_FONT_ITAL:
831                 if (currentMode() == TEXT_MODE)
832                         handleFont(cur, cmd.argument(), "textit");
833                 else
834                         handleFont(cur, cmd.argument(), "mathit");
835                 break;
836         case LFUN_FONT_NOUN:
837                 if (currentMode() == TEXT_MODE)
838                         // FIXME: should be "noun"
839                         handleFont(cur, cmd.argument(), "textsc");
840                 else
841                         handleFont(cur, cmd.argument(), "mathbb");
842                 break;
843         /*
844         case LFUN_FONT_FREE_APPLY:
845                 handleFont(cur, cmd.argument(), "textrm");
846                 break;
847         */
848         case LFUN_FONT_DEFAULT:
849                 handleFont(cur, cmd.argument(), "textnormal");
850                 break;
851
852         case LFUN_MATH_MODE: {
853 #if 1
854                 // ignore math-mode on when already in math mode
855                 if (currentMode() == Inset::MATH_MODE && cmd.argument() == "on")
856                         break;
857                 cur.macroModeClose();
858                 docstring const save_selection = grabAndEraseSelection(cur);
859                 selClearOrDel(cur);
860                 //cur.plainInsert(MathAtom(new InsetMathMBox(cur.bv())));
861                 cur.plainInsert(MathAtom(new InsetMathBox(from_ascii("mbox"))));
862                 cur.posLeft();
863                 cur.pushLeft(*cur.nextInset());
864                 cur.niceInsert(save_selection);
865 #else
866                 if (currentMode() == Inset::TEXT_MODE) {
867                         cur.niceInsert(MathAtom(new InsetMathHull("simple")));
868                         cur.message(_("create new math text environment ($...$)"));
869                 } else {
870                         handleFont(cur, cmd.argument(), "textrm");
871                         cur.message(_("entered math text mode (textrm)"));
872                 }
873 #endif
874                 break;
875         }
876
877         case LFUN_MATH_SIZE:
878 #if 0
879                 recordUndo(cur);
880                 cur.setSize(arg);
881 #endif
882                 break;
883
884         case LFUN_MATH_MATRIX: {
885                 recordUndo(cur, Undo::ATOMIC);
886                 unsigned int m = 1;
887                 unsigned int n = 1;
888                 docstring v_align;
889                 docstring h_align;
890                 idocstringstream is(cmd.argument());
891                 is >> m >> n >> v_align >> h_align;
892                 if (m < 1)
893                         m = 1;
894                 if (n < 1)
895                         n = 1;
896                 v_align += 'c';
897                 cur.niceInsert(
898                         MathAtom(new InsetMathArray(from_ascii("array"), m, n, (char)v_align[0], h_align)));
899                 break;
900         }
901
902         case LFUN_MATH_DELIM: {
903                 docstring ls;
904                 docstring rs = support::split(cmd.argument(), ls, ' ');
905                 // Reasonable default values
906                 if (ls.empty())
907                         ls = '(';
908                 if (rs.empty())
909                         rs = ')';
910                 recordUndo(cur, Undo::ATOMIC);
911                 cur.handleNest(MathAtom(new InsetMathDelim(ls, rs)));
912                 break;
913         }
914
915         case LFUN_MATH_BIGDELIM: {
916                 docstring const lname  = from_utf8(cmd.getArg(0));
917                 docstring const ldelim = from_utf8(cmd.getArg(1));
918                 docstring const rname  = from_utf8(cmd.getArg(2));
919                 docstring const rdelim = from_utf8(cmd.getArg(3));
920                 latexkeys const * l = in_word_set(lname);
921                 bool const have_l = l && l->inset == "big" &&
922                                     InsetMathBig::isBigInsetDelim(ldelim);
923                 l = in_word_set(rname);
924                 bool const have_r = l && l->inset == "big" &&
925                                     InsetMathBig::isBigInsetDelim(rdelim);
926                 // We mimic LFUN_MATH_DELIM in case we have an empty left
927                 // or right delimiter.
928                 if (have_l || have_r) {
929                         recordUndo(cur, Undo::ATOMIC);
930                         docstring const selection = grabAndEraseSelection(cur);
931                         selClearOrDel(cur);
932                         if (have_l)
933                                 cur.insert(MathAtom(new InsetMathBig(lname,
934                                                                 ldelim)));
935                         cur.niceInsert(selection);
936                         if (have_r)
937                                 cur.insert(MathAtom(new InsetMathBig(rname,
938                                                                 rdelim)));
939                 }
940                 // Don't call cur.undispatched() if we did nothing, this would
941                 // lead to infinite recursion via Text::dispatch().
942                 break;
943         }
944
945         case LFUN_SPACE_INSERT:
946         case LFUN_MATH_SPACE:
947                 recordUndo(cur, Undo::ATOMIC);
948                 cur.insert(MathAtom(new InsetMathSpace(from_ascii(","))));
949                 break;
950
951         case LFUN_ERT_INSERT:
952                 // interpret this as if a backslash was typed
953                 recordUndo(cur, Undo::ATOMIC);
954                 interpretChar(cur, '\\');
955                 break;
956
957         case LFUN_MATH_SUBSCRIPT:
958                 // interpret this as if a _ was typed
959                 recordUndo(cur, Undo::ATOMIC);
960                 interpretChar(cur, '_');
961                 break;
962
963         case LFUN_MATH_SUPERSCRIPT:
964                 // interpret this as if a ^ was typed
965                 recordUndo(cur, Undo::ATOMIC);
966                 interpretChar(cur, '^');
967                 break;
968
969         case LFUN_QUOTE_INSERT:
970                 // interpret this as if a straight " was typed
971                 recordUndo(cur, Undo::ATOMIC);
972                 interpretChar(cur, '\"');
973                 break;
974
975 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
976 // handling such that "self-insert" works on "arbitrary stuff" too, and
977 // math-insert only handles special math things like "matrix".
978         case LFUN_MATH_INSERT: {
979                 recordUndo(cur, Undo::ATOMIC);
980                 if (cmd.argument() == "^" || cmd.argument() == "_") {
981                         interpretChar(cur, cmd.argument()[0]);
982                 } else
983                         cur.niceInsert(cmd.argument());
984                 break;
985                 }
986
987         case LFUN_DIALOG_SHOW_NEW_INSET: {
988                 docstring const & name = cmd.argument();
989                 string data;
990                 if (name == "ref") {
991                         InsetMathRef tmp(name);
992                         data = tmp.createDialogStr(to_utf8(name));
993                 }
994                 cur.bv().showInsetDialog(to_utf8(name), data, 0);
995                 break;
996         }
997
998         case LFUN_INSET_INSERT: {
999                 MathData ar;
1000                 if (createInsetMath_fromDialogStr(cmd.argument(), ar)) {
1001                         recordUndo(cur);
1002                         cur.insert(ar);
1003                 } else
1004                         cur.undispatched();
1005                 break;
1006         }
1007
1008         default:
1009                 InsetMath::doDispatch(cur, cmd);
1010                 break;
1011         }
1012 }
1013
1014
1015 bool InsetMathNest::getStatus(Cursor & cur, FuncRequest const & cmd,
1016                 FuncStatus & flag) const
1017 {
1018         // the font related toggles
1019         //string tc = "mathnormal";
1020         bool ret = true;
1021         string const arg = to_utf8(cmd.argument());
1022         switch (cmd.action) {
1023         case LFUN_TABULAR_FEATURE:
1024                 flag.enabled(false);
1025                 break;
1026 #if 0
1027         case LFUN_TABULAR_FEATURE:
1028                 // FIXME: check temporarily disabled
1029                 // valign code
1030                 char align = mathcursor::valign();
1031                 if (align == '\0') {
1032                         enable = false;
1033                         break;
1034                 }
1035                 if (cmd.argument().empty()) {
1036                         flag.clear();
1037                         break;
1038                 }
1039                 if (!contains("tcb", cmd.argument()[0])) {
1040                         enable = false;
1041                         break;
1042                 }
1043                 flag.setOnOff(cmd.argument()[0] == align);
1044                 break;
1045 #endif
1046         /// We have to handle them since 1.4 blocks all unhandled actions
1047         case LFUN_FONT_ITAL:
1048         case LFUN_FONT_BOLD:
1049         case LFUN_FONT_SANS:
1050         case LFUN_FONT_EMPH:
1051         case LFUN_FONT_TYPEWRITER:
1052         case LFUN_FONT_NOUN:
1053         case LFUN_FONT_ROMAN:
1054         case LFUN_FONT_DEFAULT:
1055                 flag.enabled(true);
1056                 break;
1057         case LFUN_MATH_MUTATE:
1058                 //flag.setOnOff(mathcursor::formula()->hullType() == to_utf8(cmd.argument()));
1059                 flag.setOnOff(false);
1060                 break;
1061
1062         // we just need to be in math mode to enable that
1063         case LFUN_MATH_SIZE:
1064         case LFUN_MATH_SPACE:
1065         case LFUN_MATH_LIMITS:
1066         case LFUN_MATH_NONUMBER:
1067         case LFUN_MATH_NUMBER:
1068         case LFUN_MATH_EXTERN:
1069                 flag.enabled(true);
1070                 break;
1071
1072         case LFUN_FONT_FRAK:
1073                 flag.enabled(currentMode() != TEXT_MODE);
1074                 break;
1075
1076         case LFUN_MATH_INSERT: {
1077                 bool const textarg =
1078                         arg == "\\textbf"   || arg == "\\textsf" ||
1079                         arg == "\\textrm"   || arg == "\\textmd" ||
1080                         arg == "\\textit"   || arg == "\\textsc" ||
1081                         arg == "\\textsl"   || arg == "\\textup" ||
1082                         arg == "\\texttt"   || arg == "\\textbb" ||
1083                         arg == "\\textnormal";
1084                 flag.enabled(currentMode() != TEXT_MODE || textarg);
1085                 break;
1086         }
1087
1088         case LFUN_MATH_MATRIX:
1089                 flag.enabled(currentMode() == MATH_MODE);
1090                 break;
1091
1092         case LFUN_INSET_INSERT: {
1093                 // Don't test createMathInset_fromDialogStr(), since
1094                 // getStatus is not called with a valid reference and the
1095                 // dialog would not be applyable.
1096                 string const name = cmd.getArg(0);
1097                 flag.enabled(name == "ref");
1098                 break;
1099         }
1100
1101         case LFUN_MATH_DELIM:
1102         case LFUN_MATH_BIGDELIM:
1103                 // Don't do this with multi-cell selections
1104                 flag.enabled(cur.selBegin().idx() == cur.selEnd().idx());
1105                 break;
1106
1107         case LFUN_HYPHENATION_POINT_INSERT:
1108         case LFUN_LIGATURE_BREAK_INSERT:
1109         case LFUN_MENU_SEPARATOR_INSERT:
1110         case LFUN_DOTS_INSERT:
1111         case LFUN_END_OF_SENTENCE_PERIOD_INSERT:
1112                 // FIXME: These would probably make sense in math-text mode
1113                 flag.enabled(false);
1114                 break;
1115
1116         default:
1117                 ret = false;
1118                 break;
1119         }
1120         return ret;
1121 }
1122
1123
1124 void InsetMathNest::edit(Cursor & cur, bool left)
1125 {
1126         cur.push(*this);
1127         cur.idx() = left ? 0 : cur.lastidx();
1128         cur.pos() = left ? 0 : cur.lastpos();
1129         cur.resetAnchor();
1130         //lyxerr << "InsetMathNest::edit, cur:\n" << cur << endl;
1131 }
1132
1133
1134 Inset * InsetMathNest::editXY(Cursor & cur, int x, int y)
1135 {
1136         int idx_min = 0;
1137         int dist_min = 1000000;
1138         for (idx_type i = 0, n = nargs(); i != n; ++i) {
1139                 int const d = cell(i).dist(cur.bv(), x, y);
1140                 if (d < dist_min) {
1141                         dist_min = d;
1142                         idx_min = i;
1143                 }
1144         }
1145         MathData & ar = cell(idx_min);
1146         cur.push(*this);
1147         cur.idx() = idx_min;
1148         cur.pos() = ar.x2pos(x - ar.xo(cur.bv()));
1149
1150         //lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
1151         if (dist_min == 0) {
1152                 // hit inside cell
1153                 for (pos_type i = 0, n = ar.size(); i < n; ++i)
1154                         if (ar[i]->covers(cur.bv(), x, y))
1155                                 return ar[i].nucleus()->editXY(cur, x, y);
1156         }
1157         return this;
1158 }
1159
1160
1161 void InsetMathNest::lfunMousePress(Cursor & cur, FuncRequest & cmd)
1162 {
1163         //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
1164         BufferView & bv = cur.bv();
1165         bv.mouseSetCursor(cur);
1166         if (cmd.button() == mouse_button::button1) {
1167                 //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
1168                 // Update the cursor update flags as needed:
1169                 //
1170                 // Update::Decoration: tells to update the decoration
1171                 //                     (visual box corners that define
1172                 //                     the inset)/
1173                 // Update::FitCursor: adjust the screen to the cursor
1174                 //                    position if needed
1175                 // cur.result().update(): don't overwrite previously set flags.
1176                 cur.updateFlags(Update::Decoration | Update::FitCursor 
1177                                 | cur.result().update());
1178         } else if (cmd.button() == mouse_button::button2) {
1179                 if (cap::selection()) {
1180                         // See comment in Text::dispatch why we do this
1181                         cap::copySelectionToStack();
1182                         cmd = FuncRequest(LFUN_PASTE, "0");
1183                         doDispatch(bv.cursor(), cmd);
1184                 } else {
1185                         MathData ar;
1186                         asArray(theSelection().get(), ar);
1187                         bv.cursor().insert(ar);
1188                 }
1189         }
1190 }
1191
1192
1193 void InsetMathNest::lfunMouseMotion(Cursor & cur, FuncRequest & cmd)
1194 {
1195         // only select with button 1
1196         if (cmd.button() == mouse_button::button1) {
1197                 Cursor & bvcur = cur.bv().cursor();
1198                 if (bvcur.anchor_.hasPart(cur)) {
1199                         //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
1200                         bvcur.setCursor(cur);
1201                         bvcur.selection() = true;
1202                         //lyxerr << "MOTION " << bvcur << endl;
1203                 } else
1204                         cur.undispatched();
1205         }
1206 }
1207
1208
1209 void InsetMathNest::lfunMouseRelease(Cursor & cur, FuncRequest & cmd)
1210 {
1211         //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
1212
1213         if (cmd.button() == mouse_button::button1) {
1214                 if (!cur.selection())
1215                         cur.noUpdate();
1216                 else {
1217                         Cursor & bvcur = cur.bv().cursor();
1218                         bvcur.selection() = true;
1219                 }
1220                 return;
1221         }
1222
1223         cur.undispatched();
1224 }
1225
1226
1227 bool InsetMathNest::interpretChar(Cursor & cur, char_type c)
1228 {
1229         //lyxerr << "interpret 2: '" << c << "'" << endl;
1230         docstring save_selection;
1231         if (c == '^' || c == '_')
1232                 save_selection = grabAndEraseSelection(cur);
1233
1234         cur.clearTargetX();
1235
1236         // handle macroMode
1237         if (cur.inMacroMode()) {
1238                 docstring name = cur.macroName();
1239
1240                 /// are we currently typing '#1' or '#2' or...?
1241                 if (name == "\\#") {
1242                         cur.backspace();
1243                         int n = c - '0';
1244                         if (n >= 1 && n <= 9)
1245                                 cur.insert(new MathMacroArgument(n));
1246                         return true;
1247                 }
1248
1249                 if (isAlphaASCII(c)) {
1250                         cur.activeMacro()->setName(name + docstring(1, c));
1251                         return true;
1252                 }
1253
1254                 // handle 'special char' macros
1255                 if (name == "\\") {
1256                         // remove the '\\'
1257                         if (c == '\\') {
1258                                 cur.backspace();
1259                                 if (currentMode() == InsetMath::TEXT_MODE)
1260                                         cur.niceInsert(createInsetMath("textbackslash"));
1261                                 else
1262                                         cur.niceInsert(createInsetMath("backslash"));
1263                         } else if (c == '{') {
1264                                 cur.backspace();
1265                                 cur.niceInsert(MathAtom(new InsetMathBrace));
1266                         } else if (c == '%') {
1267                                 cur.backspace();
1268                                 cur.niceInsert(MathAtom(new InsetMathComment));
1269                         } else if (c == '#') {
1270                                 BOOST_ASSERT(cur.activeMacro());
1271                                 cur.activeMacro()->setName(name + docstring(1, c));
1272                         } else {
1273                                 cur.backspace();
1274                                 cur.niceInsert(createInsetMath(docstring(1, c)));
1275                         }
1276                         return true;
1277                 }
1278
1279                 // One character big delimiters. The others are handled in
1280                 // interpretString().
1281                 latexkeys const * l = in_word_set(name.substr(1));
1282                 if (name[0] == '\\' && l && l->inset == "big") {
1283                         docstring delim;
1284                         switch (c) {
1285                         case '{':
1286                                 delim = from_ascii("\\{");
1287                                 break;
1288                         case '}':
1289                                 delim = from_ascii("\\}");
1290                                 break;
1291                         default:
1292                                 delim = docstring(1, c);
1293                                 break;
1294                         }
1295                         if (InsetMathBig::isBigInsetDelim(delim)) {
1296                                 // name + delim ared a valid InsetMathBig.
1297                                 // We can't use cur.macroModeClose() because
1298                                 // it does not handle delim.
1299                                 InsetMathUnknown * p = cur.activeMacro();
1300                                 p->finalize();
1301                                 --cur.pos();
1302                                 cur.cell().erase(cur.pos());
1303                                 cur.plainInsert(MathAtom(
1304                                         new InsetMathBig(name.substr(1), delim)));
1305                                 return true;
1306                         }
1307                 }
1308
1309                 // leave macro mode and try again if necessary
1310                 cur.macroModeClose();
1311                 if (c == '{')
1312                         cur.niceInsert(MathAtom(new InsetMathBrace));
1313                 else if (c != ' ')
1314                         interpretChar(cur, c);
1315                 return true;
1316         }
1317
1318         // This is annoying as one has to press <space> far too often.
1319         // Disable it.
1320
1321 #if 0
1322                 // leave autocorrect mode if necessary
1323                 if (autocorrect() && c == ' ') {
1324                         autocorrect() = false;
1325                         return true;
1326                 }
1327 #endif
1328
1329         // just clear selection on pressing the space bar
1330         if (cur.selection() && c == ' ') {
1331                 cur.selection() = false;
1332                 return true;
1333         }
1334
1335         selClearOrDel(cur);
1336
1337         if (c == '\\') {
1338                 //lyxerr << "starting with macro" << endl;
1339                 cur.insert(MathAtom(new InsetMathUnknown(from_ascii("\\"), false)));
1340                 return true;
1341         }
1342
1343         if (c == '\n') {
1344                 if (currentMode() == InsetMath::TEXT_MODE)
1345                         cur.insert(c);
1346                 return true;
1347         }
1348
1349         if (c == ' ') {
1350                 if (currentMode() == InsetMath::TEXT_MODE) {
1351                         // insert spaces in text mode,
1352                         // but suppress direct insertion of two spaces in a row
1353                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1354                         // it is better than nothing...
1355                         if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ') {
1356                                 cur.insert(c);
1357                                 // FIXME: we have to enable full redraw here because of the
1358                                 // visual box corners that define the inset. If we know for
1359                                 // sure that we stay within the same cell we can optimize for
1360                                 // that using:
1361                                 //cur.updateFlags(Update::SinglePar | Update::FitCursor);
1362                         }
1363                         return true;
1364                 }
1365                 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1366                         cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1367                         // FIXME: we have to enable full redraw here because of the
1368                         // visual box corners that define the inset. If we know for
1369                         // sure that we stay within the same cell we can optimize for
1370                         // that using:
1371                         //cur.updateFlags(Update::SinglePar | Update::FitCursor);
1372                         return true;
1373                 }
1374
1375                 if (cur.popRight()) {
1376                         // FIXME: we have to enable full redraw here because of the
1377                         // visual box corners that define the inset. If we know for
1378                         // sure that we stay within the same cell we can optimize for
1379                         // that using:
1380                         //cur.updateFlags(Update::FitCursor);
1381                         return true;
1382                 }
1383
1384                 // if we are at the very end, leave the formula
1385                 return cur.pos() != cur.lastpos();
1386         }
1387
1388         // These shouldn't work in text mode:
1389         if (currentMode() != InsetMath::TEXT_MODE) {
1390                 if (c == '_') {
1391                         script(cur, false, save_selection);
1392                         return true;
1393                 }
1394                 if (c == '^') {
1395                         script(cur, true, save_selection);
1396                         return true;
1397                 }
1398                 if (c == '~') {
1399                         cur.niceInsert(createInsetMath("sim"));
1400                         return true;
1401                 }
1402         }
1403
1404         if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' ||
1405             c == '%' || c == '_' || c == '^') {
1406                 cur.niceInsert(createInsetMath(docstring(1, c)));
1407                 return true;
1408         }
1409
1410
1411         // try auto-correction
1412         //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1413         //      return true;
1414
1415         // no special circumstances, so insert the character without any fuss
1416         cur.insert(c);
1417         cur.autocorrect() = true;
1418         return true;
1419 }
1420
1421
1422 bool InsetMathNest::interpretString(Cursor & cur, docstring const & str)
1423 {
1424         // Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
1425         // possible
1426         if (!cur.empty() && cur.pos() > 0 &&
1427             cur.cell()[cur.pos() - 1]->asUnknownInset()) {
1428                 if (InsetMathBig::isBigInsetDelim(str)) {
1429                         docstring prev = asString(cur.cell()[cur.pos() - 1]);
1430                         if (prev[0] == '\\') {
1431                                 prev = prev.substr(1);
1432                                 latexkeys const * l = in_word_set(prev);
1433                                 if (l && l->inset == "big") {
1434                                         cur.cell()[cur.pos() - 1] =
1435                                                 MathAtom(new InsetMathBig(prev, str));
1436                                         return true;
1437                                 }
1438                         }
1439                 }
1440         }
1441         return false;
1442 }
1443
1444
1445 bool InsetMathNest::script(Cursor & cur, bool up,
1446                 docstring const & save_selection)
1447 {
1448         // Hack to get \^ and \_ working
1449         //lyxerr << "handling script: up: " << up << endl;
1450         if (cur.inMacroMode() && cur.macroName() == "\\") {
1451                 if (up)
1452                         cur.niceInsert(createInsetMath("mathcircumflex"));
1453                 else
1454                         interpretChar(cur, '_');
1455                 return true;
1456         }
1457
1458         cur.macroModeClose();
1459         if (asScriptInset() && cur.idx() == 0) {
1460                 // we are in a nucleus of a script inset, move to _our_ script
1461                 InsetMathScript * inset = asScriptInset();
1462                 //lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
1463                 inset->ensure(up);
1464                 cur.idx() = inset->idxOfScript(up);
1465                 cur.pos() = 0;
1466         } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1467                 --cur.pos();
1468                 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
1469                 cur.push(*inset);
1470                 inset->ensure(up);
1471                 cur.idx() = inset->idxOfScript(up);
1472                 cur.pos() = cur.lastpos();
1473         } else {
1474                 // convert the thing to our left to a scriptinset or create a new
1475                 // one if in the very first position of the array
1476                 if (cur.pos() == 0) {
1477                         //lyxerr << "new scriptinset" << endl;
1478                         cur.insert(new InsetMathScript(up));
1479                 } else {
1480                         //lyxerr << "converting prev atom " << endl;
1481                         cur.prevAtom() = MathAtom(new InsetMathScript(cur.prevAtom(), up));
1482                 }
1483                 --cur.pos();
1484                 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
1485                 // See comment in MathParser.cpp for special handling of {}-bases
1486
1487                 cur.push(*inset);
1488                 cur.idx() = 1;
1489                 cur.pos() = 0;
1490         }
1491         //lyxerr << "inserting selection 1:\n" << save_selection << endl;
1492         cur.niceInsert(save_selection);
1493         cur.resetAnchor();
1494         //lyxerr << "inserting selection 2:\n" << save_selection << endl;
1495         return true;
1496 }
1497
1498
1499 } // namespace lyx