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