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