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