]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathNest.C
Add margin to paragraph dialog.
[lyx.git] / src / mathed / InsetMathNest.C
1 /**
2  * \file InsetMathNest.C
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 "MathData.h"
22 #include "InsetMathDelim.h"
23 #include "MathFactory.h"
24 #include "InsetMathHull.h"
25 #include "MathStream.h"
26 #include "MathMacroArgument.h"
27 //#include "InsetMathMBox.h"
28 #include "MathParser.h"
29 #include "InsetMathScript.h"
30 #include "InsetMathSpace.h"
31 #include "InsetMathSymbol.h"
32 #include "MathSupport.h"
33 #include "InsetMathUnknown.h"
34 #include "InsetMathRef.h"
35
36 #include "BufferView.h"
37 #include "CutAndPaste.h"
38 #include "FuncStatus.h"
39 #include "LColor.h"
40 #include "bufferview_funcs.h"
41 #include "coordcache.h"
42 #include "cursor.h"
43 #include "debug.h"
44 #include "dispatchresult.h"
45 #include "funcrequest.h"
46 #include "gettext.h"
47 #include "lyxtext.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 #include "lyxserver.h"
60 #include "lyxsocket.h"
61
62 #include <sstream>
63
64
65 namespace lyx {
66
67 using cap::copySelection;
68 using cap::grabAndEraseSelection;
69 using cap::cutSelection;
70 using cap::replaceSelection;
71 using cap::selClearOrDel;
72
73 using std::endl;
74 using std::string;
75 using std::istringstream;
76
77
78 InsetMathNest::InsetMathNest(idx_type nargs)
79         : cells_(nargs), lock_(false)
80 {}
81
82
83 InsetMath::idx_type InsetMathNest::nargs() const
84 {
85         return cells_.size();
86 }
87
88
89 MathArray & InsetMathNest::cell(idx_type i)
90 {
91         return cells_[i];
92 }
93
94
95 MathArray const & InsetMathNest::cell(idx_type i) const
96 {
97         return cells_[i];
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 MathArray::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         MathArray 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 MathArray "
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(LCursor & 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(LCursor & cur) const
165 {
166         return idxNext(cur);
167 }
168
169
170 bool InsetMathNest::idxPrev(LCursor & 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(LCursor & cur) const
182 {
183         return idxPrev(cur);
184 }
185
186
187 bool InsetMathNest::idxFirst(LCursor & 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(LCursor & 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                                         LColor::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         LCursor & 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                 MathArray 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, LColor::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                                 MathArray 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, LColor::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(MathArray 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 MathArray InsetMathNest::glue() const
321 {
322         MathArray 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(LCursor & /*cur*/)
362 {
363 #ifdef WITH_WARNINGS
364 #warning look here
365 #endif
366 #if 0
367         MathArray & 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                         MathArray 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         (LCursor & cur, docstring const & arg, char const * const font)
396 {
397         handleFont(cur, arg, from_ascii(font));
398 }
399
400
401 void InsetMathNest::handleFont
402         (LCursor & 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(LCursor & cur, docstring const & arg)
419 {
420         recordUndo(cur, Undo::ATOMIC);
421         LyXFont font;
422         bool b;
423         bv_funcs::string2font(to_utf8(arg), font, b);
424         if (font.color() != LColor::inherit) {
425                 MathAtom at = MathAtom(new InsetMathColor(true, font.color()));
426                 cur.handleNest(at, 0);
427         }
428 }
429
430
431 void InsetMathNest::doDispatch(LCursor & 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_FINISHED_UP:
494                 cur.bv().cursor() = cur;
495                 break;
496
497         case LFUN_FINISHED_DOWN:
498                 ++cur.pos();
499                 cur.bv().cursor() = cur;
500                 break;
501
502         case LFUN_CHAR_FORWARD:
503                 cur.updateFlags(Update::Decoration | Update::FitCursor);
504         case LFUN_CHAR_FORWARD_SELECT:
505                 cur.selHandle(cmd.action == LFUN_CHAR_FORWARD_SELECT);
506                 cur.autocorrect() = false;
507                 cur.clearTargetX();
508                 cur.macroModeClose();
509                 if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
510                         cur.pushLeft(*cur.nextAtom().nucleus());
511                         cur.inset().idxFirst(cur);
512                 } else if (cur.posRight() || idxRight(cur)
513                         || cur.popRight() || cur.selection())
514                         ;
515                 else {
516                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
517                         cur.undispatched();
518                 }
519                 break;
520
521         case LFUN_CHAR_BACKWARD:
522                 cur.updateFlags(Update::Decoration | Update::FitCursor);
523         case LFUN_CHAR_BACKWARD_SELECT:
524                 cur.selHandle(cmd.action == LFUN_CHAR_BACKWARD_SELECT);
525                 cur.autocorrect() = false;
526                 cur.clearTargetX();
527                 cur.macroModeClose();
528                 if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
529                         cur.posLeft();
530                         cur.push(*cur.nextAtom().nucleus());
531                         cur.inset().idxLast(cur);
532                 } else if (cur.posLeft() || idxLeft(cur)
533                         || cur.popLeft() || cur.selection())
534                         ;
535                 else {
536                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
537                         cur.undispatched();
538                 }
539                 break;
540
541         case LFUN_UP:
542                 cur.updateFlags(Update::Decoration | Update::FitCursor);
543         case LFUN_UP_SELECT:
544                 // FIXME Tried to use clearTargetX and macroModeClose, crashed on cur.up()
545                 if (cur.inMacroMode()) {
546                         // Make Helge happy
547                         cur.macroModeClose();
548                         break;
549                 }
550                 cur.selHandle(cmd.action == LFUN_UP_SELECT);
551                 if (!cur.up()) {
552                         cmd = FuncRequest(LFUN_FINISHED_UP);
553                         cur.undispatched();
554                 }
555                 // fixes bug 1598. Please check!
556                 cur.normalize();
557                 break;
558
559         case LFUN_DOWN:
560                 cur.updateFlags(Update::Decoration | Update::FitCursor);
561         case LFUN_DOWN_SELECT:
562                 if (cur.inMacroMode()) {
563                         cur.macroModeClose();
564                         break;
565                 }
566                 cur.selHandle(cmd.action == LFUN_DOWN_SELECT);
567                 if (!cur.down()) {
568                         cmd = FuncRequest(LFUN_FINISHED_DOWN);
569                         cur.undispatched();
570                 }
571                 // fixes bug 1598. Please check!
572                 cur.normalize();
573                 break;
574
575         case LFUN_MOUSE_DOUBLE:
576         case LFUN_MOUSE_TRIPLE:
577         case LFUN_WORD_SELECT:
578                 cur.pos() = 0;
579                 cur.idx() = 0;
580                 cur.resetAnchor();
581                 cur.selection() = true;
582                 cur.pos() = cur.lastpos();
583                 cur.idx() = cur.lastidx();
584                 cap::saveSelection(cur);
585                 break;
586
587         case LFUN_PARAGRAPH_UP:
588         case LFUN_PARAGRAPH_DOWN:
589                 cur.updateFlags(Update::Decoration | Update::FitCursor);
590         case LFUN_PARAGRAPH_UP_SELECT:
591         case LFUN_PARAGRAPH_DOWN_SELECT:
592                 break;
593
594         case LFUN_LINE_BEGIN:
595         case LFUN_WORD_BACKWARD:
596                 cur.updateFlags(Update::Decoration | Update::FitCursor);
597         case LFUN_LINE_BEGIN_SELECT:
598         case LFUN_WORD_BACKWARD_SELECT:
599                 cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT ||
600                                 cmd.action == LFUN_LINE_BEGIN_SELECT);
601                 cur.macroModeClose();
602                 if (cur.pos() != 0) {
603                         cur.pos() = 0;
604                 } else if (cur.col() != 0) {
605                         cur.idx() -= cur.col();
606                         cur.pos() = 0;
607                 } else if (cur.idx() != 0) {
608                         cur.idx() = 0;
609                         cur.pos() = 0;
610                 } else {
611                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
612                         cur.undispatched();
613                 }
614                 break;
615
616         case LFUN_WORD_FORWARD:
617         case LFUN_LINE_END:
618                 cur.updateFlags(Update::Decoration | Update::FitCursor);
619         case LFUN_WORD_FORWARD_SELECT:
620         case LFUN_LINE_END_SELECT:
621                 cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT ||
622                                 cmd.action == LFUN_LINE_END_SELECT);
623                 cur.macroModeClose();
624                 cur.clearTargetX();
625                 if (cur.pos() != cur.lastpos()) {
626                         cur.pos() = cur.lastpos();
627                 } else if (ncols() && (cur.col() != cur.lastcol())) {
628                         cur.idx() = cur.idx() - cur.col() + cur.lastcol();
629                         cur.pos() = cur.lastpos();
630                 } else if (cur.idx() != cur.lastidx()) {
631                         cur.idx() = cur.lastidx();
632                         cur.pos() = cur.lastpos();
633                 } else {
634                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
635                         cur.undispatched();
636                 }
637                 break;
638
639         case LFUN_SCREEN_UP_SELECT:
640         case LFUN_SCREEN_UP:
641                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
642                 cur.undispatched();
643                 break;
644
645         case LFUN_SCREEN_DOWN_SELECT:
646         case LFUN_SCREEN_DOWN:
647                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
648                 cur.undispatched();
649                 break;
650
651         case LFUN_CELL_FORWARD:
652                 cur.updateFlags(Update::Decoration | Update::FitCursor);
653                 cur.inset().idxNext(cur);
654                 break;
655
656         case LFUN_CELL_BACKWARD:
657                 cur.updateFlags(Update::Decoration | Update::FitCursor);
658                 cur.inset().idxPrev(cur);
659                 break;
660
661         case LFUN_WORD_DELETE_BACKWARD:
662         case LFUN_CHAR_DELETE_BACKWARD:
663                 if (cur.pos() == 0)
664                         // May affect external cell:
665                         recordUndoInset(cur, Undo::ATOMIC);
666                 else
667                         recordUndo(cur, Undo::ATOMIC);
668                 // if the inset can not be removed from within, delete it
669                 if (!cur.backspace()) {
670                         FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
671                         cur.innerText()->dispatch(cur, cmd);
672                 }
673                 break;
674
675         case LFUN_WORD_DELETE_FORWARD:
676         case LFUN_CHAR_DELETE_FORWARD:
677                 if (cur.pos() == cur.lastpos())
678                         // May affect external cell:
679                         recordUndoInset(cur, Undo::ATOMIC);
680                 else
681                         recordUndo(cur, Undo::ATOMIC);
682                 // if the inset can not be removed from within, delete it
683                 if (!cur.erase()) {
684                         FuncRequest cmd = FuncRequest(LFUN_CHAR_DELETE_FORWARD);
685                         cur.innerText()->dispatch(cur, cmd);
686                 }
687                 break;
688
689         case LFUN_ESCAPE:
690                 if (cur.selection())
691                         cur.clearSelection();
692                 else  {
693                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
694                         cur.undispatched();
695                 }
696                 break;
697
698         case LFUN_INSET_TOGGLE:
699                 recordUndo(cur);
700                 lock(!lock());
701                 cur.popRight();
702                 break;
703
704         case LFUN_SELF_INSERT:
705                 if (cmd.argument().size() != 1) {
706                         recordUndo(cur);
707                         docstring const arg = cmd.argument();
708                         if (!interpretString(cur, arg))
709                                 cur.insert(arg);
710                         break;
711                 }
712                 // Don't record undo steps if we are in macro mode and
713                 // cmd.argument is the next character of the macro name.
714                 // Otherwise we'll get an invalid cursor if we undo after
715                 // the macro was finished and the macro is a known command,
716                 // e.g. sqrt. LCursor::macroModeClose replaces in this case
717                 // the InsetMathUnknown with name "frac" by an empty
718                 // InsetMathFrac -> a pos value > 0 is invalid.
719                 // A side effect is that an undo before the macro is finished
720                 // undoes the complete macro, not only the last character.
721                 if (!cur.inMacroMode())
722                         recordUndo(cur);
723
724                 // spacial handling of space. If we insert an inset
725                 // via macro mode, we want to put the cursor inside it
726                 // if relevant. Think typing "\frac<space>".
727                 if (cmd.argument()[0] == ' '
728                     && cur.inMacroMode() && cur.macroName() != "\\"
729                     && cur.macroModeClose()) {
730                         MathAtom const atom = cur.prevAtom();
731                         if (atom->asNestInset() && atom->nargs() > 0) {
732                                 cur.posLeft();
733                                 cur.pushLeft(*cur.nextInset());
734                         }
735                 } else if (!interpretChar(cur, cmd.argument()[0])) {
736                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
737                         cur.undispatched();
738                 }
739                 break;
740
741         //case LFUN_SERVER_GET_XY:
742         //      sprintf(dispatch_buffer, "%d %d",);
743         //      break;
744
745         case LFUN_SERVER_SET_XY: {
746                 lyxerr << "LFUN_SERVER_SET_XY broken!" << endl;
747                 int x = 0;
748                 int y = 0;
749                 istringstream is(to_utf8(cmd.argument()));
750                 is >> x >> y;
751                 cur.setScreenPos(x, y);
752                 break;
753         }
754
755         // Special casing for superscript in case of LyX handling
756         // dead-keys:
757         case LFUN_ACCENT_CIRCUMFLEX:
758                 if (cmd.argument().empty()) {
759                         // do superscript if LyX handles
760                         // deadkeys
761                         recordUndo(cur, Undo::ATOMIC);
762                         script(cur, true, grabAndEraseSelection(cur));
763                 }
764                 break;
765
766         case LFUN_ACCENT_UMLAUT:
767         case LFUN_ACCENT_ACUTE:
768         case LFUN_ACCENT_GRAVE:
769         case LFUN_ACCENT_BREVE:
770         case LFUN_ACCENT_DOT:
771         case LFUN_ACCENT_MACRON:
772         case LFUN_ACCENT_CARON:
773         case LFUN_ACCENT_TILDE:
774         case LFUN_ACCENT_CEDILLA:
775         case LFUN_ACCENT_CIRCLE:
776         case LFUN_ACCENT_UNDERDOT:
777         case LFUN_ACCENT_TIE:
778         case LFUN_ACCENT_OGONEK:
779         case LFUN_ACCENT_HUNGARIAN_UMLAUT:
780                 break;
781
782         //  Math fonts
783         case LFUN_FONT_FREE_APPLY:
784         case LFUN_FONT_FREE_UPDATE:
785                 handleFont2(cur, cmd.argument());
786                 break;
787
788         case LFUN_FONT_BOLD:
789                 if (currentMode() == TEXT_MODE)
790                         handleFont(cur, cmd.argument(), "textbf");
791                 else
792                         handleFont(cur, cmd.argument(), "mathbf");
793                 break;
794         case LFUN_FONT_SANS:
795                 if (currentMode() == TEXT_MODE)
796                         handleFont(cur, cmd.argument(), "textsf");
797                 else
798                         handleFont(cur, cmd.argument(), "mathsf");
799                 break;
800         case LFUN_FONT_EMPH:
801                 if (currentMode() == TEXT_MODE)
802                         handleFont(cur, cmd.argument(), "emph");
803                 else
804                         handleFont(cur, cmd.argument(), "mathcal");
805                 break;
806         case LFUN_FONT_ROMAN:
807                 if (currentMode() == TEXT_MODE)
808                         handleFont(cur, cmd.argument(), "textrm");
809                 else
810                         handleFont(cur, cmd.argument(), "mathrm");
811                 break;
812         case LFUN_FONT_CODE:
813                 if (currentMode() == TEXT_MODE)
814                         handleFont(cur, cmd.argument(), "texttt");
815                 else
816                         handleFont(cur, cmd.argument(), "mathtt");
817                 break;
818         case LFUN_FONT_FRAK:
819                 handleFont(cur, cmd.argument(), "mathfrak");
820                 break;
821         case LFUN_FONT_ITAL:
822                 if (currentMode() == TEXT_MODE)
823                         handleFont(cur, cmd.argument(), "textit");
824                 else
825                         handleFont(cur, cmd.argument(), "mathit");
826                 break;
827         case LFUN_FONT_NOUN:
828                 if (currentMode() == TEXT_MODE)
829                         // FIXME: should be "noun"
830                         handleFont(cur, cmd.argument(), "textsc");
831                 else
832                         handleFont(cur, cmd.argument(), "mathbb");
833                 break;
834         /*
835         case LFUN_FONT_FREE_APPLY:
836                 handleFont(cur, cmd.argument(), "textrm");
837                 break;
838         */
839         case LFUN_FONT_DEFAULT:
840                 handleFont(cur, cmd.argument(), "textnormal");
841                 break;
842
843         case LFUN_MATH_MODE: {
844 #if 1
845                 // ignore math-mode on when already in math mode
846                 if (currentMode() == InsetBase::MATH_MODE && cmd.argument() == "on")
847                         break;
848                 cur.macroModeClose();
849                 docstring const save_selection = grabAndEraseSelection(cur);
850                 selClearOrDel(cur);
851                 //cur.plainInsert(MathAtom(new InsetMathMBox(cur.bv())));
852                 cur.plainInsert(MathAtom(new InsetMathBox(from_ascii("mbox"))));
853                 cur.posLeft();
854                 cur.pushLeft(*cur.nextInset());
855                 cur.niceInsert(save_selection);
856 #else
857                 if (currentMode() == InsetBase::TEXT_MODE) {
858                         cur.niceInsert(MathAtom(new InsetMathHull("simple")));
859                         cur.message(_("create new math text environment ($...$)"));
860                 } else {
861                         handleFont(cur, cmd.argument(), "textrm");
862                         cur.message(_("entered math text mode (textrm)"));
863                 }
864 #endif
865                 break;
866         }
867
868         case LFUN_MATH_SIZE:
869 #if 0
870                 recordUndo(cur);
871                 cur.setSize(arg);
872 #endif
873                 break;
874
875         case LFUN_MATH_MATRIX: {
876                 recordUndo(cur, Undo::ATOMIC);
877                 unsigned int m = 1;
878                 unsigned int n = 1;
879                 docstring v_align;
880                 docstring h_align;
881                 idocstringstream is(cmd.argument());
882                 is >> m >> n >> v_align >> h_align;
883                 if (m < 1)
884                         m = 1;
885                 if (n < 1)
886                         n = 1;
887                 v_align += 'c';
888                 cur.niceInsert(
889                         MathAtom(new InsetMathArray(from_ascii("array"), m, n, (char)v_align[0], h_align)));
890                 break;
891         }
892
893         case LFUN_MATH_DELIM: {
894                 docstring ls;
895                 docstring rs = support::split(cmd.argument(), ls, ' ');
896                 // Reasonable default values
897                 if (ls.empty())
898                         ls = '(';
899                 if (rs.empty())
900                         rs = ')';
901                 recordUndo(cur, Undo::ATOMIC);
902                 cur.handleNest(MathAtom(new InsetMathDelim(ls, rs)));
903                 break;
904         }
905
906         case LFUN_MATH_BIGDELIM: {
907                 docstring const lname  = from_utf8(cmd.getArg(0));
908                 docstring const ldelim = from_utf8(cmd.getArg(1));
909                 docstring const rname  = from_utf8(cmd.getArg(2));
910                 docstring const rdelim = from_utf8(cmd.getArg(3));
911                 latexkeys const * l = in_word_set(lname);
912                 bool const have_l = l && l->inset == "big" &&
913                                     InsetMathBig::isBigInsetDelim(ldelim);
914                 l = in_word_set(rname);
915                 bool const have_r = l && l->inset == "big" &&
916                                     InsetMathBig::isBigInsetDelim(rdelim);
917                 // We mimic LFUN_MATH_DELIM in case we have an empty left
918                 // or right delimiter.
919                 if (have_l || have_r) {
920                         recordUndo(cur, Undo::ATOMIC);
921                         docstring const selection = grabAndEraseSelection(cur);
922                         selClearOrDel(cur);
923                         if (have_l)
924                                 cur.insert(MathAtom(new InsetMathBig(lname,
925                                                                 ldelim)));
926                         cur.niceInsert(selection);
927                         if (have_r)
928                                 cur.insert(MathAtom(new InsetMathBig(rname,
929                                                                 rdelim)));
930                 }
931                 // Don't call cur.undispatched() if we did nothing, this would
932                 // lead to infinite recursion via LyXText::dispatch().
933                 break;
934         }
935
936         case LFUN_SPACE_INSERT:
937         case LFUN_MATH_SPACE:
938                 recordUndo(cur, Undo::ATOMIC);
939                 cur.insert(MathAtom(new InsetMathSpace(from_ascii(","))));
940                 break;
941
942         case LFUN_ERT_INSERT:
943                 // interpret this as if a backslash was typed
944                 recordUndo(cur, Undo::ATOMIC);
945                 interpretChar(cur, '\\');
946                 break;
947
948         case LFUN_MATH_SUBSCRIPT:
949                 // interpret this as if a _ was typed
950                 recordUndo(cur, Undo::ATOMIC);
951                 interpretChar(cur, '_');
952                 break;
953
954         case LFUN_MATH_SUPERSCRIPT:
955                 // interpret this as if a ^ was typed
956                 recordUndo(cur, Undo::ATOMIC);
957                 interpretChar(cur, '^');
958                 break;
959
960         case LFUN_QUOTE_INSERT:
961                 // interpret this as if a straight " was typed
962                 recordUndo(cur, Undo::ATOMIC);
963                 interpretChar(cur, '\"');
964                 break;
965
966 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
967 // handling such that "self-insert" works on "arbitrary stuff" too, and
968 // math-insert only handles special math things like "matrix".
969         case LFUN_MATH_INSERT: {
970                 recordUndo(cur, Undo::ATOMIC);
971                 if (cmd.argument() == "^" || cmd.argument() == "_") {
972                         interpretChar(cur, cmd.argument()[0]);
973                 } else
974                         cur.niceInsert(cmd.argument());
975                 break;
976                 }
977
978         case LFUN_DIALOG_SHOW_NEW_INSET: {
979                 docstring const & name = cmd.argument();
980                 string data;
981                 if (name == "ref") {
982                         RefInset tmp(name);
983                         data = tmp.createDialogStr(to_utf8(name));
984                 }
985                 cur.bv().showInsetDialog(to_utf8(name), data, 0);
986                 break;
987         }
988
989         case LFUN_INSET_INSERT: {
990                 MathArray ar;
991                 if (createInsetMath_fromDialogStr(cmd.argument(), ar)) {
992                         recordUndo(cur);
993                         cur.insert(ar);
994                 } else
995                         cur.undispatched();
996                 break;
997         }
998
999         default:
1000                 InsetMathDim::doDispatch(cur, cmd);
1001                 break;
1002         }
1003 }
1004
1005
1006 bool InsetMathNest::getStatus(LCursor & cur, FuncRequest const & cmd,
1007                 FuncStatus & flag) const
1008 {
1009         // the font related toggles
1010         //string tc = "mathnormal";
1011         bool ret = true;
1012         string const arg = to_utf8(cmd.argument());
1013         switch (cmd.action) {
1014         case LFUN_TABULAR_FEATURE:
1015                 flag.enabled(false);
1016                 break;
1017 #if 0
1018         case LFUN_TABULAR_FEATURE:
1019                 // FIXME: check temporarily disabled
1020                 // valign code
1021                 char align = mathcursor::valign();
1022                 if (align == '\0') {
1023                         enable = false;
1024                         break;
1025                 }
1026                 if (cmd.argument().empty()) {
1027                         flag.clear();
1028                         break;
1029                 }
1030                 if (!contains("tcb", cmd.argument()[0])) {
1031                         enable = false;
1032                         break;
1033                 }
1034                 flag.setOnOff(cmd.argument()[0] == align);
1035                 break;
1036 #endif
1037         /// We have to handle them since 1.4 blocks all unhandled actions
1038         case LFUN_FONT_ITAL:
1039         case LFUN_FONT_BOLD:
1040         case LFUN_FONT_SANS:
1041         case LFUN_FONT_EMPH:
1042         case LFUN_FONT_CODE:
1043         case LFUN_FONT_NOUN:
1044         case LFUN_FONT_ROMAN:
1045         case LFUN_FONT_DEFAULT:
1046                 flag.enabled(true);
1047                 break;
1048         case LFUN_MATH_MUTATE:
1049                 //flag.setOnOff(mathcursor::formula()->hullType() == to_utf8(cmd.argument()));
1050                 flag.setOnOff(false);
1051                 break;
1052
1053         // we just need to be in math mode to enable that
1054         case LFUN_MATH_SIZE:
1055         case LFUN_MATH_SPACE:
1056         case LFUN_MATH_LIMITS:
1057         case LFUN_MATH_NONUMBER:
1058         case LFUN_MATH_NUMBER:
1059         case LFUN_MATH_EXTERN:
1060                 flag.enabled(true);
1061                 break;
1062
1063         case LFUN_FONT_FRAK:
1064                 flag.enabled(currentMode() != TEXT_MODE);
1065                 break;
1066
1067         case LFUN_MATH_INSERT: {
1068                 bool const textarg =
1069                         arg == "\\textbf"   || arg == "\\textsf" ||
1070                         arg == "\\textrm"   || arg == "\\textmd" ||
1071                         arg == "\\textit"   || arg == "\\textsc" ||
1072                         arg == "\\textsl"   || arg == "\\textup" ||
1073                         arg == "\\texttt"   || arg == "\\textbb" ||
1074                         arg == "\\textnormal";
1075                 flag.enabled(currentMode() != TEXT_MODE || textarg);
1076                 break;
1077         }
1078
1079         case LFUN_MATH_MATRIX:
1080                 flag.enabled(currentMode() == MATH_MODE);
1081                 break;
1082
1083         case LFUN_INSET_INSERT: {
1084                 // Don't test createMathInset_fromDialogStr(), since
1085                 // getStatus is not called with a valid reference and the
1086                 // dialog would not be applyable.
1087                 string const name = cmd.getArg(0);
1088                 flag.enabled(name == "ref");
1089                 break;
1090         }
1091
1092         case LFUN_MATH_DELIM:
1093         case LFUN_MATH_BIGDELIM:
1094                 // Don't do this with multi-cell selections
1095                 flag.enabled(cur.selBegin().idx() == cur.selEnd().idx());
1096                 break;
1097
1098         default:
1099                 ret = false;
1100                 break;
1101         }
1102         return ret;
1103 }
1104
1105
1106 void InsetMathNest::edit(LCursor & cur, bool left)
1107 {
1108         cur.push(*this);
1109         cur.idx() = left ? 0 : cur.lastidx();
1110         cur.pos() = left ? 0 : cur.lastpos();
1111         cur.resetAnchor();
1112         //lyxerr << "InsetMathNest::edit, cur:\n" << cur << endl;
1113 }
1114
1115
1116 InsetBase * InsetMathNest::editXY(LCursor & cur, int x, int y)
1117 {
1118         int idx_min = 0;
1119         int dist_min = 1000000;
1120         for (idx_type i = 0, n = nargs(); i != n; ++i) {
1121                 int const d = cell(i).dist(cur.bv(), x, y);
1122                 if (d < dist_min) {
1123                         dist_min = d;
1124                         idx_min = i;
1125                 }
1126         }
1127         MathArray & ar = cell(idx_min);
1128         cur.push(*this);
1129         cur.idx() = idx_min;
1130         cur.pos() = ar.x2pos(x - ar.xo(cur.bv()));
1131
1132         //lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
1133         if (dist_min == 0) {
1134                 // hit inside cell
1135                 for (pos_type i = 0, n = ar.size(); i < n; ++i)
1136                         if (ar[i]->covers(cur.bv(), x, y))
1137                                 return ar[i].nucleus()->editXY(cur, x, y);
1138         }
1139         return this;
1140 }
1141
1142
1143 void InsetMathNest::lfunMousePress(LCursor & cur, FuncRequest & cmd)
1144 {
1145         //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
1146         BufferView & bv = cur.bv();
1147         if (cmd.button() == mouse_button::button1) {
1148                 //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
1149                 bv.mouseSetCursor(cur);
1150                 // Update the cursor update flags as needed:
1151                 //
1152                 // Update::Decoration: tells to update the decoration (visual box
1153                 //                     corners that define the inset)/
1154                 // Update::FitCursor: adjust the screen to the cursor position if
1155                 //                    needed
1156                 // cur.result().update(): don't overwrite previously set flags.
1157                 cur.updateFlags(Update::Decoration | Update::FitCursor | cur.result().update());
1158         } else if (cmd.button() == mouse_button::button2) {
1159                 MathArray ar;
1160                 if (cap::selection()) {
1161                         // See comment in LyXText::dispatch why we do this
1162                         cap::copySelectionToStack();
1163                         cmd = FuncRequest(LFUN_PASTE, "0");
1164                         doDispatch(cur, cmd);
1165                 } else
1166                         asArray(theSelection().get(), ar);
1167
1168                 cur.insert(ar);
1169                 bv.mouseSetCursor(cur);
1170         }
1171 }
1172
1173
1174 void InsetMathNest::lfunMouseMotion(LCursor & cur, FuncRequest & cmd)
1175 {
1176         // only select with button 1
1177         if (cmd.button() == mouse_button::button1) {
1178                 LCursor & bvcur = cur.bv().cursor();
1179                 if (bvcur.anchor_.hasPart(cur)) {
1180                         //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
1181                         bvcur.setCursor(cur);
1182                         bvcur.selection() = true;
1183                         //lyxerr << "MOTION " << bvcur << endl;
1184                 } else
1185                         cur.undispatched();
1186         }
1187 }
1188
1189
1190 void InsetMathNest::lfunMouseRelease(LCursor & cur, FuncRequest & cmd)
1191 {
1192         //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
1193
1194         if (cmd.button() == mouse_button::button1) {
1195                 if (!cur.selection())
1196                         cur.noUpdate();
1197                 else {
1198                         LCursor & bvcur = cur.bv().cursor();
1199                         bvcur.selection() = true;
1200                         cap::saveSelection(bvcur);
1201                 }
1202                 return;
1203         }
1204
1205         if (cmd.button() == mouse_button::button3) {
1206                 // try to dispatch to enclosed insets first
1207                 cur.bv().showDialog("mathpanel");
1208                 return;
1209         }
1210
1211         cur.undispatched();
1212 }
1213
1214
1215 bool InsetMathNest::interpretChar(LCursor & cur, char_type c)
1216 {
1217         //lyxerr << "interpret 2: '" << c << "'" << endl;
1218         docstring save_selection;
1219         if (c == '^' || c == '_')
1220                 save_selection = grabAndEraseSelection(cur);
1221
1222         cur.clearTargetX();
1223
1224         // handle macroMode
1225         if (cur.inMacroMode()) {
1226                 docstring name = cur.macroName();
1227
1228                 /// are we currently typing '#1' or '#2' or...?
1229                 if (name == "\\#") {
1230                         cur.backspace();
1231                         int n = c - '0';
1232                         if (n >= 1 && n <= 9)
1233                                 cur.insert(new MathMacroArgument(n));
1234                         return true;
1235                 }
1236
1237                 if (isAlphaASCII(c)) {
1238                         cur.activeMacro()->setName(name + docstring(1, c));
1239                         return true;
1240                 }
1241
1242                 // handle 'special char' macros
1243                 if (name == "\\") {
1244                         // remove the '\\'
1245                         if (c == '\\') {
1246                                 cur.backspace();
1247                                 if (currentMode() == InsetMath::TEXT_MODE)
1248                                         cur.niceInsert(createInsetMath("textbackslash"));
1249                                 else
1250                                         cur.niceInsert(createInsetMath("backslash"));
1251                         } else if (c == '{') {
1252                                 cur.backspace();
1253                                 cur.niceInsert(MathAtom(new InsetMathBrace));
1254                         } else if (c == '%') {
1255                                 cur.backspace();
1256                                 cur.niceInsert(MathAtom(new InsetMathComment));
1257                         } else if (c == '#') {
1258                                 BOOST_ASSERT(cur.activeMacro());
1259                                 cur.activeMacro()->setName(name + docstring(1, c));
1260                         } else {
1261                                 cur.backspace();
1262                                 cur.niceInsert(createInsetMath(docstring(1, c)));
1263                         }
1264                         return true;
1265                 }
1266
1267                 // One character big delimiters. The others are handled in
1268                 // interpretString().
1269                 latexkeys const * l = in_word_set(name.substr(1));
1270                 if (name[0] == '\\' && l && l->inset == "big") {
1271                         docstring delim;
1272                         switch (c) {
1273                         case '{':
1274                                 delim = from_ascii("\\{");
1275                                 break;
1276                         case '}':
1277                                 delim = from_ascii("\\}");
1278                                 break;
1279                         default:
1280                                 delim = docstring(1, c);
1281                                 break;
1282                         }
1283                         if (InsetMathBig::isBigInsetDelim(delim)) {
1284                                 // name + delim ared a valid InsetMathBig.
1285                                 // We can't use cur.macroModeClose() because
1286                                 // it does not handle delim.
1287                                 InsetMathUnknown * p = cur.activeMacro();
1288                                 p->finalize();
1289                                 --cur.pos();
1290                                 cur.cell().erase(cur.pos());
1291                                 cur.plainInsert(MathAtom(
1292                                         new InsetMathBig(name.substr(1), delim)));
1293                                 return true;
1294                         }
1295                 }
1296
1297                 // leave macro mode and try again if necessary
1298                 cur.macroModeClose();
1299                 if (c == '{')
1300                         cur.niceInsert(MathAtom(new InsetMathBrace));
1301                 else if (c != ' ')
1302                         interpretChar(cur, c);
1303                 return true;
1304         }
1305
1306         // This is annoying as one has to press <space> far too often.
1307         // Disable it.
1308
1309 #if 0
1310                 // leave autocorrect mode if necessary
1311                 if (autocorrect() && c == ' ') {
1312                         autocorrect() = false;
1313                         return true;
1314                 }
1315 #endif
1316
1317         // just clear selection on pressing the space bar
1318         if (cur.selection() && c == ' ') {
1319                 cur.selection() = false;
1320                 return true;
1321         }
1322
1323         selClearOrDel(cur);
1324
1325         if (c == '\\') {
1326                 //lyxerr << "starting with macro" << endl;
1327                 cur.insert(MathAtom(new InsetMathUnknown(from_ascii("\\"), false)));
1328                 return true;
1329         }
1330
1331         if (c == '\n') {
1332                 if (currentMode() == InsetMath::TEXT_MODE)
1333                         cur.insert(c);
1334                 return true;
1335         }
1336
1337         if (c == ' ') {
1338                 if (currentMode() == InsetMath::TEXT_MODE) {
1339                         // insert spaces in text mode,
1340                         // but suppress direct insertion of two spaces in a row
1341                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1342                         // it is better than nothing...
1343                         if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ') {
1344                                 cur.insert(c);
1345                                 // FIXME: we have to enable full redraw here because of the
1346                                 // visual box corners that define the inset. If we know for
1347                                 // sure that we stay within the same cell we can optimize for
1348                                 // that using:
1349                                 //cur.updateFlags(Update::SinglePar | Update::FitCursor);
1350                         }
1351                         return true;
1352                 }
1353                 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1354                         cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1355                         // FIXME: we have to enable full redraw here because of the
1356                         // visual box corners that define the inset. If we know for
1357                         // sure that we stay within the same cell we can optimize for
1358                         // that using:
1359                         //cur.updateFlags(Update::SinglePar | Update::FitCursor);
1360                         return true;
1361                 }
1362
1363                 if (cur.popRight()) {
1364                         // FIXME: we have to enable full redraw here because of the
1365                         // visual box corners that define the inset. If we know for
1366                         // sure that we stay within the same cell we can optimize for
1367                         // that using:
1368                         //cur.updateFlags(Update::FitCursor);
1369                         return true;
1370                 }
1371
1372                 // if we are at the very end, leave the formula
1373                 return cur.pos() != cur.lastpos();
1374         }
1375
1376         // These shouldn't work in text mode:
1377         if (currentMode() != InsetMath::TEXT_MODE) {
1378                 if (c == '_') {
1379                         script(cur, false, save_selection);
1380                         return true;
1381                 }
1382                 if (c == '^') {
1383                         script(cur, true, save_selection);
1384                         return true;
1385                 }
1386                 if (c == '~') {
1387                         cur.niceInsert(createInsetMath("sim"));
1388                         return true;
1389                 }
1390         }
1391
1392         if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' ||
1393             c == '%' || c == '_' || c == '^') {
1394                 cur.niceInsert(createInsetMath(docstring(1, c)));
1395                 return true;
1396         }
1397
1398
1399         // try auto-correction
1400         //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1401         //      return true;
1402
1403         // no special circumstances, so insert the character without any fuss
1404         cur.insert(c);
1405         cur.autocorrect() = true;
1406         return true;
1407 }
1408
1409
1410 bool InsetMathNest::interpretString(LCursor & cur, docstring const & str)
1411 {
1412         // Create a InsetMathBig from cur.cell()[cur.pos() - 1] and t if
1413         // possible
1414         if (!cur.empty() && cur.pos() > 0 &&
1415             cur.cell()[cur.pos() - 1]->asUnknownInset()) {
1416                 if (InsetMathBig::isBigInsetDelim(str)) {
1417                         docstring prev = asString(cur.cell()[cur.pos() - 1]);
1418                         if (prev[0] == '\\') {
1419                                 prev = prev.substr(1);
1420                                 latexkeys const * l = in_word_set(prev);
1421                                 if (l && l->inset == "big") {
1422                                         cur.cell()[cur.pos() - 1] =
1423                                                 MathAtom(new InsetMathBig(prev, str));
1424                                         return true;
1425                                 }
1426                         }
1427                 }
1428         }
1429         return false;
1430 }
1431
1432
1433 bool InsetMathNest::script(LCursor & cur, bool up, 
1434                 docstring const & save_selection)
1435 {
1436         // Hack to get \^ and \_ working
1437         //lyxerr << "handling script: up: " << up << endl;
1438         if (cur.inMacroMode() && cur.macroName() == "\\") {
1439                 if (up)
1440                         cur.niceInsert(createInsetMath("mathcircumflex"));
1441                 else
1442                         interpretChar(cur, '_');
1443                 return true;
1444         }
1445
1446         cur.macroModeClose();
1447         if (asScriptInset() && cur.idx() == 0) {
1448                 // we are in a nucleus of a script inset, move to _our_ script
1449                 InsetMathScript * inset = asScriptInset();
1450                 //lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
1451                 inset->ensure(up);
1452                 cur.idx() = inset->idxOfScript(up);
1453                 cur.pos() = 0;
1454         } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1455                 --cur.pos();
1456                 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
1457                 cur.push(*inset);
1458                 inset->ensure(up);
1459                 cur.idx() = inset->idxOfScript(up);
1460                 cur.pos() = cur.lastpos();
1461         } else {
1462                 // convert the thing to our left to a scriptinset or create a new
1463                 // one if in the very first position of the array
1464                 if (cur.pos() == 0) {
1465                         //lyxerr << "new scriptinset" << endl;
1466                         cur.insert(new InsetMathScript(up));
1467                 } else {
1468                         //lyxerr << "converting prev atom " << endl;
1469                         cur.prevAtom() = MathAtom(new InsetMathScript(cur.prevAtom(), up));
1470                 }
1471                 --cur.pos();
1472                 InsetMathScript * inset = cur.nextAtom().nucleus()->asScriptInset();
1473                 // See comment in MathParser.C for special handling of {}-bases
1474
1475                 cur.push(*inset);
1476                 cur.idx() = 1;
1477                 cur.pos() = 0;
1478         }
1479         //lyxerr << "inserting selection 1:\n" << save_selection << endl;
1480         cur.niceInsert(save_selection);
1481         cur.resetAnchor();
1482         //lyxerr << "inserting selection 2:\n" << save_selection << endl;
1483         return true;
1484 }
1485
1486
1487 } // namespace lyx