]> git.lyx.org Git - lyx.git/blob - src/mathed/math_nestinset.C
Fix math cursor positioning bug
[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_boxinset.h"
17 #include "math_braceinset.h"
18 #include "math_commentinset.h"
19 #include "math_data.h"
20 #include "math_deliminset.h"
21 #include "math_factory.h"
22 #include "math_hullinset.h"
23 #include "math_mathmlstream.h"
24 #include "math_macroarg.h"
25 //#include "math_mboxinset.h"
26 #include "math_parser.h"
27 #include "math_scriptinset.h"
28 #include "math_spaceinset.h"
29 #include "math_symbolinset.h"
30 #include "math_support.h"
31 #include "math_unknowninset.h"
32
33 #include "BufferView.h"
34 #include "CutAndPaste.h"
35 #include "FuncStatus.h"
36 #include "LColor.h"
37 #include "bufferview_funcs.h"
38 #include "coordcache.h"
39 #include "cursor.h"
40 #include "debug.h"
41 #include "dispatchresult.h"
42 #include "funcrequest.h"
43 #include "gettext.h"
44 #include "outputparams.h"
45 #include "undo.h"
46
47 #include "support/lstrings.h"
48
49 #include "frontends/Dialogs.h"
50 #include "frontends/LyXView.h"
51 #include "frontends/Painter.h"
52
53 #include <sstream>
54
55 using lyx::cap::copySelection;
56 using lyx::cap::grabAndEraseSelection;
57 using lyx::cap::cutSelection;
58 using lyx::cap::pasteSelection;
59 using lyx::cap::replaceSelection;
60 using lyx::cap::selClearOrDel;
61
62 using std::endl;
63 using std::string;
64 using std::istringstream;
65
66
67 MathNestInset::MathNestInset(idx_type nargs)
68         : cells_(nargs), lock_(false)
69 {}
70
71
72 MathInset::idx_type MathNestInset::nargs() const
73 {
74         return cells_.size();
75 }
76
77
78 MathArray & MathNestInset::cell(idx_type i)
79 {
80         return cells_[i];
81 }
82
83
84 MathArray const & MathNestInset::cell(idx_type i) const
85 {
86         return cells_[i];
87 }
88
89
90 void MathNestInset::getCursorPos(CursorSlice const & sl,
91         int & x, int & y) const
92 {
93 // FIXME: This is a hack. Ideally, the coord cache should not store
94 // absolute positions, but relative ones. This would mean to call
95 // setXY() not in MathArray::draw(), but in the parent insets' draw()
96 // with the correctly adjusted x,y values. But this means that we'd have
97 // to touch all (math)inset's draw() methods. Right now, we'll store
98 // absolute value, and make them here relative, only to make them
99 // absolute again when actually drawing the cursor. What a mess.
100         BOOST_ASSERT(ptr_cmp(&sl.inset(), this));
101         MathArray const & ar = sl.cell();
102         if (!theCoords.getArrays().has(&ar)) {
103                 // this can (semi-)legally happen if we just created this cell
104                 // and it never has been drawn before. So don't ASSERT.
105                 //lyxerr << "no cached data for array " << &ar << endl;
106                 x = 0;
107                 y = 0;
108                 return;
109         }
110         Point const pt = theCoords.getArrays().xy(&ar);
111         if (!theCoords.getInsets().has(this)) {
112                 // same as above
113                 //lyxerr << "no cached data for inset " << this << endl;
114                 x = 0;
115                 y = 0;
116                 return;
117         }
118         Point const pt2 = theCoords.getInsets().xy(this);
119         //lyxerr << "retrieving position cache for MathArray "
120         //      << pt.x_ << ' ' << pt.y_ << std::endl;
121         x = pt.x_ - pt2.x_ + ar.pos2x(sl.pos());
122         y = pt.y_ - pt2.y_;
123 //      lyxerr << "pt.y_ : " << pt.y_ << " pt2_.y_ : " << pt2.y_
124 //              << " asc: " << ascent() << "  des: " << descent()
125 //              << " ar.asc: " << ar.ascent() << " ar.des: " << ar.descent() << endl;
126         // move cursor visually into empty cells ("blue rectangles");
127         if (ar.empty())
128                 x += 2;
129 }
130
131 void MathNestInset::metrics(MetricsInfo const & mi) const
132 {
133         MetricsInfo m = mi;
134         for (idx_type i = 0, n = nargs(); i != n; ++i)
135                 cell(i).metrics(m);
136 }
137
138
139 bool MathNestInset::idxNext(LCursor & cur) const
140 {
141         BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
142         if (cur.idx() == cur.lastidx())
143                 return false;
144         ++cur.idx();
145         cur.pos() = 0;
146         return true;
147 }
148
149
150 bool MathNestInset::idxRight(LCursor & cur) const
151 {
152         return idxNext(cur);
153 }
154
155
156 bool MathNestInset::idxPrev(LCursor & cur) const
157 {
158         BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
159         if (cur.idx() == 0)
160                 return false;
161         --cur.idx();
162         cur.pos() = cur.lastpos();
163         return true;
164 }
165
166
167 bool MathNestInset::idxLeft(LCursor & cur) const
168 {
169         return idxPrev(cur);
170 }
171
172
173 bool MathNestInset::idxFirst(LCursor & cur) const
174 {
175         BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
176         if (nargs() == 0)
177                 return false;
178         cur.idx() = 0;
179         cur.pos() = 0;
180         return true;
181 }
182
183
184 bool MathNestInset::idxLast(LCursor & cur) const
185 {
186         BOOST_ASSERT(ptr_cmp(&cur.inset(), this));
187         if (nargs() == 0)
188                 return false;
189         cur.idx() = cur.lastidx();
190         cur.pos() = cur.lastpos();
191         return true;
192 }
193
194
195 void MathNestInset::dump() const
196 {
197         WriteStream os(lyxerr);
198         os << "---------------------------------------------\n";
199         write(os);
200         os << "\n";
201         for (idx_type i = 0, n = nargs(); i != n; ++i)
202                 os << cell(i) << "\n";
203         os << "---------------------------------------------\n";
204 }
205
206
207 void MathNestInset::draw(PainterInfo & pi, int x, int y) const
208 {
209 #if 0
210         if (lock_)
211                 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
212                                         LColor::mathlockbg);
213 #endif
214         setPosCache(pi, x, y);
215 }
216
217
218 void MathNestInset::drawSelection(PainterInfo & pi, int x, int y) const
219 {
220         // FIXME: hack to get position cache warm
221         draw(pi, x, y);
222
223         // this should use the x/y values given, not the cached values
224         LCursor & cur = pi.base.bv->cursor();
225         if (!cur.selection())
226                 return;
227         if (!ptr_cmp(&cur.inset(), this))
228                 return;
229
230         CursorSlice s1 = cur.selBegin();
231         CursorSlice s2 = cur.selEnd();
232         //lyxerr << "MathNestInset::drawing selection: "
233         //      << " s1: " << s1 << " s2: " << s2 << endl;
234         if (s1.idx() == s2.idx()) {
235                 MathArray const & c = cell(s1.idx());
236                 int x1 = c.xo() + c.pos2x(s1.pos());
237                 int y1 = c.yo() - c.ascent();
238                 int x2 = c.xo() + c.pos2x(s2.pos());
239                 int y2 = c.yo() + c.descent();
240                 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
241         //lyxerr << "MathNestInset::drawing selection 3: "
242         //      << " x1: " << x1 << " x2: " << x2
243         //      << " y1: " << y1 << " y2: " << y2 << endl;
244         } else {
245                 for (idx_type i = 0; i < nargs(); ++i) {
246                         if (idxBetween(i, s1.idx(), s2.idx())) {
247                                 MathArray const & c = cell(i);
248                                 int x1 = c.xo();
249                                 int y1 = c.yo() - c.ascent();
250                                 int x2 = c.xo() + c.width();
251                                 int y2 = c.yo() + c.descent();
252                                 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
253                         }
254                 }
255         }
256 }
257
258
259 void MathNestInset::validate(LaTeXFeatures & features) const
260 {
261         for (idx_type i = 0; i < nargs(); ++i)
262                 cell(i).validate(features);
263 }
264
265
266 void MathNestInset::replace(ReplaceData & rep)
267 {
268         for (idx_type i = 0; i < nargs(); ++i)
269                 cell(i).replace(rep);
270 }
271
272
273 bool MathNestInset::contains(MathArray const & ar) const
274 {
275         for (idx_type i = 0; i < nargs(); ++i)
276                 if (cell(i).contains(ar))
277                         return true;
278         return false;
279 }
280
281
282 bool MathNestInset::lock() const
283 {
284         return lock_;
285 }
286
287
288 void MathNestInset::lock(bool l)
289 {
290         lock_ = l;
291 }
292
293
294 bool MathNestInset::isActive() const
295 {
296         return nargs() > 0;
297 }
298
299
300 MathArray MathNestInset::glue() const
301 {
302         MathArray ar;
303         for (size_t i = 0; i < nargs(); ++i)
304                 ar.append(cell(i));
305         return ar;
306 }
307
308
309 void MathNestInset::write(WriteStream & os) const
310 {
311         os << '\\' << name().c_str();
312         for (size_t i = 0; i < nargs(); ++i)
313                 os << '{' << cell(i) << '}';
314         if (nargs() == 0)
315                 os.pendingSpace(true);
316         if (lock_ && !os.latex()) {
317                 os << "\\lyxlock";
318                 os.pendingSpace(true);
319         }
320 }
321
322
323 void MathNestInset::normalize(NormalStream & os) const
324 {
325         os << '[' << name().c_str();
326         for (size_t i = 0; i < nargs(); ++i)
327                 os << ' ' << cell(i);
328         os << ']';
329 }
330
331
332 int MathNestInset::latex(Buffer const &, std::ostream & os,
333                         OutputParams const & runparams) const
334 {
335         WriteStream wi(os, runparams.moving_arg, true);
336         write(wi);
337         return wi.line();
338 }
339
340
341 void MathNestInset::notifyCursorLeaves(LCursor & /*cur*/)
342 {
343 #ifdef WITH_WARNINGS
344 #warning look here
345 #endif
346 #if 0
347         MathArray & ar = cur.cell();
348         // remove base-only "scripts"
349         for (pos_type i = 0; i + 1 < ar.size(); ++i) {
350                 MathScriptInset * p = operator[](i).nucleus()->asScriptInset();
351                 if (p && p->nargs() == 1) {
352                         MathArray ar = p->nuc();
353                         erase(i);
354                         insert(i, ar);
355                         cur.adjust(i, ar.size() - 1);
356                 }
357         }
358
359         // glue adjacent font insets of the same kind
360         for (pos_type i = 0; i + 1 < size(); ++i) {
361                 MathFontInset * p = operator[](i).nucleus()->asFontInset();
362                 MathFontInset const * q = operator[](i + 1)->asFontInset();
363                 if (p && q && p->name() == q->name()) {
364                         p->cell(0).append(q->cell(0));
365                         erase(i + 1);
366                         cur.adjust(i, -1);
367                 }
368         }
369 #endif
370 }
371
372
373 void MathNestInset::handleFont
374         (LCursor & cur, string const & arg, string const & font)
375 {
376         // this whole function is a hack and won't work for incremental font
377         // changes...
378         recordUndo(cur, Undo::ATOMIC);
379
380         if (cur.inset().asMathInset()->name() == font)
381                 cur.handleFont(font);
382         else {
383                 cur.handleNest(createMathInset(font));
384                 cur.insert(arg);
385         }
386 }
387
388
389 void MathNestInset::handleFont2(LCursor & cur, string const & arg)
390 {
391         recordUndo(cur, Undo::ATOMIC);
392         LyXFont font;
393         bool b;
394         bv_funcs::string2font(arg, font, b);
395         if (font.color() != LColor::inherit) {
396                 MathAtom at = createMathInset("color");
397                 asArray(lcolor.getGUIName(font.color()), at.nucleus()->cell(0));
398                 cur.handleNest(at, 1);
399         }
400 }
401
402
403 void MathNestInset::doDispatch(LCursor & cur, FuncRequest & cmd)
404 {
405         //lyxerr << "MathNestInset: request: " << cmd << std::endl;
406         //CursorSlice sl = cur.current();
407
408         switch (cmd.action) {
409
410         case LFUN_PASTE: {
411                 recordUndo(cur);
412                 cur.message(_("Paste"));
413                 replaceSelection(cur);
414                 size_t n = 0;
415                 istringstream is(cmd.argument);
416                 is >> n;
417                 pasteSelection(cur, n);
418                 cur.clearSelection(); // bug 393
419                 cur.bv().switchKeyMap();
420                 finishUndo();
421                 break;
422         }
423
424         case LFUN_CUT:
425                 cutSelection(cur, true, true);
426                 cur.message(_("Cut"));
427                 break;
428
429         case LFUN_COPY:
430                 copySelection(cur);
431                 cur.message(_("Copy"));
432                 break;
433
434         case LFUN_MOUSE_PRESS:
435                 lfunMousePress(cur, cmd);
436                 break;
437
438         case LFUN_MOUSE_MOTION:
439                 lfunMouseMotion(cur, cmd);
440                 break;
441
442         case LFUN_MOUSE_RELEASE:
443                 lfunMouseRelease(cur, cmd);
444                 break;
445
446         case LFUN_FINISHED_LEFT:
447                 cur.bv().cursor() = cur;
448                 break;
449
450         case LFUN_FINISHED_RIGHT:
451                 ++cur.pos();
452                 cur.bv().cursor() = cur;
453                 break;
454
455         case LFUN_FINISHED_UP:
456                 cur.bv().cursor() = cur;
457                 break;
458
459         case LFUN_FINISHED_DOWN:
460                 cur.bv().cursor() = cur;
461                 break;
462
463         case LFUN_RIGHTSEL:
464         case LFUN_RIGHT:
465                 cur.selHandle(cmd.action == LFUN_RIGHTSEL);
466                 cur.autocorrect() = false;
467                 cur.clearTargetX();
468                 cur.macroModeClose();
469                 if (cur.pos() != cur.lastpos() && cur.openable(cur.nextAtom())) {
470                         cur.pushLeft(*cur.nextAtom().nucleus());
471                         cur.inset().idxFirst(cur);
472                 } else if (cur.posRight() || idxRight(cur)
473                         || cur.popRight() || cur.selection())
474                         ;
475                 else
476                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
477                 break;
478
479         case LFUN_LEFTSEL:
480         case LFUN_LEFT:
481                 cur.selHandle(cmd.action == LFUN_LEFTSEL);
482                 cur.autocorrect() = false;
483                 cur.clearTargetX();
484                 cur.macroModeClose();
485                 if (cur.pos() != 0 && cur.openable(cur.prevAtom())) {
486                         cur.posLeft();
487                         cur.push(*cur.nextAtom().nucleus());
488                         cur.inset().idxLast(cur);
489                 } else if (cur.posLeft() || idxLeft(cur)
490                         || cur.popLeft() || cur.selection())
491                         ;
492                 else
493                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
494                 break;
495
496         case LFUN_UPSEL:
497         case LFUN_UP:
498                 cur.selHandle(cmd.action == LFUN_UPSEL);
499                 if (!cur.up())
500                         cmd = FuncRequest(LFUN_FINISHED_UP);
501                 // fixes bug 1598. Please check!
502                 cur.normalize();
503                 break;
504
505         case LFUN_DOWNSEL:
506         case LFUN_DOWN:
507                 cur.selHandle(cmd.action == LFUN_DOWNSEL);
508                 if (!cur.down())
509                         cmd = FuncRequest(LFUN_FINISHED_DOWN);
510                 // fixes bug 1598. Please check!
511                 cur.normalize();
512                 break;
513
514         case LFUN_MOUSE_DOUBLE:
515         case LFUN_MOUSE_TRIPLE:
516         case LFUN_WORDSEL:
517                 cur.pos() = 0;
518                 cur.idx() = 0;
519                 cur.resetAnchor();
520                 cur.selection() = true;
521                 cur.pos() = cur.lastpos();
522                 cur.idx() = cur.lastidx();
523                 break;
524
525         case LFUN_UP_PARAGRAPHSEL:
526         case LFUN_UP_PARAGRAPH:
527         case LFUN_DOWN_PARAGRAPHSEL:
528         case LFUN_DOWN_PARAGRAPH:
529                 break;
530
531         case LFUN_HOMESEL:
532         case LFUN_HOME:
533         case LFUN_WORDLEFTSEL:
534         case LFUN_WORDLEFT:
535                 cur.selHandle(cmd.action == LFUN_WORDLEFTSEL || cmd.action == LFUN_HOMESEL);
536                 cur.macroModeClose();
537                 if (cur.pos() != 0) {
538                         cur.pos() = 0;
539                 } else if (cur.col() != 0) {
540                         cur.idx() -= cur.col();
541                         cur.pos() = 0;
542                 } else if (cur.idx() != 0) {
543                         cur.idx() = 0;
544                         cur.pos() = 0;
545                 } else {
546                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
547                 }
548                 break;
549
550         case LFUN_WORDRIGHTSEL:
551         case LFUN_WORDRIGHT:
552         case LFUN_ENDSEL:
553         case LFUN_END:
554                 cur.selHandle(cmd.action == LFUN_WORDRIGHTSEL || cmd.action == LFUN_ENDSEL);
555                 cur.macroModeClose();
556                 cur.clearTargetX();
557                 if (cur.pos() != cur.lastpos()) {
558                         cur.pos() = cur.lastpos();
559                 } else if (cur.col() != cur.lastcol()) {
560                         cur.idx() = cur.idx() - cur.col() + cur.lastcol();
561                         cur.pos() = cur.lastpos();
562                 } else if (cur.idx() != cur.lastidx()) {
563                         cur.idx() = cur.lastidx();
564                         cur.pos() = cur.lastpos();
565                 } else {
566                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
567                 }
568                 break;
569
570         case LFUN_PRIORSEL:
571         case LFUN_PRIOR:
572                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
573                 break;
574
575         case LFUN_NEXTSEL:
576         case LFUN_NEXT:
577                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
578                 break;
579
580         case LFUN_CELL_FORWARD:
581                 cur.inset().idxNext(cur);
582                 break;
583
584         case LFUN_CELL_BACKWARD:
585                 cur.inset().idxPrev(cur);
586                 break;
587
588         case LFUN_DELETE_WORD_BACKWARD:
589         case LFUN_BACKSPACE:
590                 recordUndo(cur, Undo::ATOMIC);
591                 cur.backspace();
592                 break;
593
594         case LFUN_DELETE_WORD_FORWARD:
595         case LFUN_DELETE:
596                 recordUndo(cur);
597                 cur.erase();
598                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
599                 break;
600
601         case LFUN_ESCAPE:
602                 if (cur.selection())
603                         cur.clearSelection();
604                 else
605                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
606                 break;
607
608         case LFUN_INSET_TOGGLE:
609                 recordUndo(cur);
610                 //lockToggle();
611                 if (cur.pos() != cur.lastpos()) {
612                         // toggle previous inset ...
613                         cur.nextAtom().nucleus()->lock(!cur.nextAtom()->lock());
614                 } else if (cur.popLeft() && cur.pos() != cur.lastpos()) {
615                         // ... or enclosing inset if we are in the last inset position
616                         cur.nextAtom().nucleus()->lock(!cur.nextAtom()->lock());
617                         ++cur.pos();
618                 }
619                 break;
620
621         case LFUN_SELFINSERT:
622                 recordUndo(cur);
623                 if (cmd.argument.size() != 1) {
624                         cur.insert(cmd.argument);
625                         break;
626                 }
627                 if (!interpret(cur, cmd.argument[0]))
628                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
629                 break;
630
631         //case LFUN_GETXY:
632         //      sprintf(dispatch_buffer, "%d %d",);
633         //      break;
634
635         case LFUN_SETXY: {
636                 lyxerr << "LFUN_SETXY broken!" << endl;
637                 int x = 0;
638                 int y = 0;
639                 istringstream is(cmd.argument);
640                 is >> x >> y;
641                 cur.setScreenPos(x, y);
642                 break;
643         }
644
645         // Special casing for superscript in case of LyX handling
646         // dead-keys:
647         case LFUN_CIRCUMFLEX:
648                 if (cmd.argument.empty()) {
649                         // do superscript if LyX handles
650                         // deadkeys
651                         recordUndo(cur, Undo::ATOMIC);
652                         script(cur, true);
653                 }
654                 break;
655
656         case LFUN_UMLAUT:
657         case LFUN_ACUTE:
658         case LFUN_GRAVE:
659         case LFUN_BREVE:
660         case LFUN_DOT:
661         case LFUN_MACRON:
662         case LFUN_CARON:
663         case LFUN_TILDE:
664         case LFUN_CEDILLA:
665         case LFUN_CIRCLE:
666         case LFUN_UNDERDOT:
667         case LFUN_TIE:
668         case LFUN_OGONEK:
669         case LFUN_HUNG_UMLAUT:
670                 break;
671
672         //  Math fonts
673         case LFUN_FREEFONT_APPLY:
674         case LFUN_FREEFONT_UPDATE:
675                 handleFont2(cur, cmd.argument);
676                 break;
677
678         case LFUN_BOLD:
679                 handleFont(cur, cmd.argument, "mathbf");
680                 break;
681         case LFUN_SANS:
682                 handleFont(cur, cmd.argument, "mathsf");
683                 break;
684         case LFUN_EMPH:
685                 handleFont(cur, cmd.argument, "mathcal");
686                 break;
687         case LFUN_ROMAN:
688                 handleFont(cur, cmd.argument, "mathrm");
689                 break;
690         case LFUN_CODE:
691                 handleFont(cur, cmd.argument, "texttt");
692                 break;
693         case LFUN_FRAK:
694                 handleFont(cur, cmd.argument, "mathfrak");
695                 break;
696         case LFUN_ITAL:
697                 handleFont(cur, cmd.argument, "mathit");
698                 break;
699         case LFUN_NOUN:
700                 handleFont(cur, cmd.argument, "mathbb");
701                 break;
702         //case LFUN_FREEFONT_APPLY:
703                 handleFont(cur, cmd.argument, "textrm");
704                 break;
705         case LFUN_DEFAULT:
706                 handleFont(cur, cmd.argument, "textnormal");
707                 break;
708
709         case LFUN_MATH_MODE:
710 #if 1
711                 // ignore math-mode on when already in math mode
712                 if (currentMode() == InsetBase::MATH_MODE && cmd.argument == "on")
713                         break;
714                 cur.macroModeClose();
715                 selClearOrDel(cur);
716                 //cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
717                 cur.plainInsert(MathAtom(new MathBoxInset("mbox")));
718                 cur.posLeft();
719                 cur.pushLeft(*cur.nextInset());
720 #else
721                 if (currentMode() == InsetBase::TEXT_MODE) {
722                         cur.niceInsert(MathAtom(new MathHullInset("simple")));
723                         cur.message(_("create new math text environment ($...$)"));
724                 } else {
725                         handleFont(cur, cmd.argument, "textrm");
726                         cur.message(_("entered math text mode (textrm)"));
727                 }
728 #endif
729                 break;
730
731         case LFUN_MATH_SIZE:
732 #if 0
733                 recordUndo(cur);
734                 cur.setSize(arg);
735 #endif
736                 break;
737
738         case LFUN_INSERT_MATRIX: {
739                 recordUndo(cur, Undo::ATOMIC);
740                 unsigned int m = 1;
741                 unsigned int n = 1;
742                 string v_align;
743                 string h_align;
744                 istringstream is(cmd.argument);
745                 is >> m >> n >> v_align >> h_align;
746                 if (m < 1)
747                         m = 1;
748                 if (n < 1)
749                         n = 1;
750                 v_align += 'c';
751                 cur.niceInsert(
752                         MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
753                 break;
754         }
755
756         case LFUN_MATH_DELIM: {
757                 lyxerr << "MathNestInset::LFUN_MATH_DELIM" << endl;
758                 string ls;
759                 string rs = lyx::support::split(cmd.argument, ls, ' ');
760                 // Reasonable default values
761                 if (ls.empty())
762                         ls = '(';
763                 if (rs.empty())
764                         rs = ')';
765                 recordUndo(cur, Undo::ATOMIC);
766                 cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
767                 break;
768         }
769
770         case LFUN_SPACE_INSERT:
771         case LFUN_MATH_SPACE:
772                 recordUndo(cur, Undo::ATOMIC);
773                 cur.insert(MathAtom(new MathSpaceInset(",")));
774                 break;
775
776         case LFUN_INSET_ERT:
777                 // interpret this as if a backslash was typed
778                 recordUndo(cur, Undo::ATOMIC);
779                 interpret(cur, '\\');
780                 break;
781
782         case LFUN_SUBSCRIPT:
783                 // interpret this as if a _ was typed
784                 recordUndo(cur, Undo::ATOMIC);
785                 interpret(cur, '_');
786                 break;
787
788         case LFUN_SUPERSCRIPT:
789                 // interpret this as if a ^ was typed
790                 recordUndo(cur, Undo::ATOMIC);
791                 interpret(cur, '^');
792                 break;
793
794 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
795 // handling such that "self-insert" works on "arbitrary stuff" too, and
796 // math-insert only handles special math things like "matrix".
797         case LFUN_INSERT_MATH:
798                 recordUndo(cur, Undo::ATOMIC);
799                 cur.niceInsert(cmd.argument);
800                 break;
801
802         case LFUN_DIALOG_SHOW_NEW_INSET: {
803                 string const & name = cmd.argument;
804                 string data;
805 #if 0
806                 if (name == "ref") {
807                         RefInset tmp(name);
808                         data = tmp.createDialogStr(name);
809                 }
810 #endif
811                 cur.bv().owner()->getDialogs().show(name, data, 0);
812                 break;
813         }
814
815         case LFUN_INSET_APPLY: {
816                 string const name = cmd.getArg(0);
817                 InsetBase * base = cur.bv().owner()->getDialogs().getOpenInset(name);
818
819                 if (base) {
820                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
821                         base->dispatch(cur, fr);
822                         break;
823                 }
824                 MathArray ar;
825                 if (createMathInset_fromDialogStr(cmd.argument, ar)) {
826                         cur.insert(ar);
827                         break;
828                 }
829                 cur.undispatched();
830                 break;
831         }
832
833         default:
834                 MathDimInset::doDispatch(cur, cmd);
835                 break;
836         }
837 }
838
839
840 bool MathNestInset::getStatus(LCursor & /*cur*/, FuncRequest const & cmd,
841                 FuncStatus & flag) const
842 {
843         // the font related toggles
844         //string tc = "mathnormal";
845         bool ret = true;
846         switch (cmd.action) {
847 #if 0
848         case LFUN_TABULAR_FEATURE:
849                 // FIXME: check temporarily disabled
850                 // valign code
851                 char align = mathcursor::valign();
852                 if (align == '\0') {
853                         enable = false;
854                         break;
855                 }
856                 if (cmd.argument.empty()) {
857                         flag.clear();
858                         break;
859                 }
860                 if (!contains("tcb", cmd.argument[0])) {
861                         enable = false;
862                         break;
863                 }
864                 flag.setOnOff(cmd.argument[0] == align);
865                 break;
866         case LFUN_BOLD:
867                 flag.setOnOff(tc == "mathbf");
868                 break;
869         case LFUN_SANS:
870                 flag.setOnOff(tc == "mathsf");
871                 break;
872         case LFUN_EMPH:
873                 flag.setOnOff(tc == "mathcal");
874                 break;
875         case LFUN_ROMAN:
876                 flag.setOnOff(tc == "mathrm");
877                 break;
878         case LFUN_CODE:
879                 flag.setOnOff(tc == "mathtt");
880                 break;
881         case LFUN_NOUN:
882                 flag.setOnOff(tc == "mathbb");
883                 break;
884         case LFUN_DEFAULT:
885                 flag.setOnOff(tc == "mathnormal");
886                 break;
887 #endif
888         case LFUN_MATH_MUTATE:
889                 //flag.setOnOff(mathcursor::formula()->hullType() == cmd.argument);
890                 flag.setOnOff(false);
891                 break;
892
893         // we just need to be in math mode to enable that
894         case LFUN_MATH_SIZE:
895         case LFUN_MATH_SPACE:
896         case LFUN_MATH_LIMITS:
897         case LFUN_MATH_NONUMBER:
898         case LFUN_MATH_NUMBER:
899         case LFUN_MATH_EXTERN:
900                 flag.enabled(true);
901                 break;
902
903         default:
904                 ret = false;
905                 break;
906         }
907         return ret;
908 }
909
910
911 void MathNestInset::edit(LCursor & cur, bool left)
912 {
913         cur.push(*this);
914         cur.idx() = left ? 0 : cur.lastidx();
915         cur.pos() = left ? 0 : cur.lastpos();
916         cur.resetAnchor();
917         lyxerr << "MathNestInset::edit, cur:\n" << cur << endl;
918 }
919
920
921 InsetBase * MathNestInset::editXY(LCursor & cur, int x, int y) const
922 {
923         int idx_min = 0;
924         int dist_min = 1000000;
925         for (idx_type i = 0; i < nargs(); ++i) {
926                 int d = cell(i).dist(x, y);
927                 if (d < dist_min) {
928                         dist_min = d;
929                         idx_min = i;
930                 }
931         }
932         MathArray const & ar = cell(idx_min);
933         cur.push(const_cast<MathNestInset&>(*this));
934         cur.idx() = idx_min;
935         cur.pos() = ar.x2pos(x - ar.xo());
936         lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
937         if (dist_min == 0) {
938                 // hit inside cell
939                 for (pos_type i = 0, n = ar.size(); i < n; ++i)
940                         if (ar[i]->covers(x, y))
941                                 return ar[i].nucleus()->editXY(cur, x, y);
942         }
943         return const_cast<MathNestInset*>(this);
944 }
945
946
947 void MathNestInset::lfunMousePress(LCursor & cur, FuncRequest & cmd)
948 {
949         //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
950         if (cmd.button() == mouse_button::button1) {
951                 //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
952                 cur.resetAnchor();
953                 cur.bv().cursor() = cur;
954         }
955
956         if (cmd.button() == mouse_button::button2) {
957                 cur.dispatch(FuncRequest(LFUN_PASTESELECTION));
958         }
959 }
960
961
962 void MathNestInset::lfunMouseMotion(LCursor & cur, FuncRequest & cmd)
963 {
964         // only select with button 1
965         if (cmd.button() == mouse_button::button1) {
966                 LCursor & bvcur = cur.bv().cursor();
967                 if (bvcur.anchor_.hasPart(cur)) {
968                         //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
969                         bvcur.setCursor(cur);
970                         bvcur.selection() = true;
971                         //lyxerr << "MOTION " << bvcur << endl;
972                 }
973                 else {
974                         cur.undispatched();
975                 }
976         }
977 }
978
979
980 void MathNestInset::lfunMouseRelease(LCursor & cur, FuncRequest & cmd)
981 {
982         //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
983
984         if (cmd.button() == mouse_button::button1) {
985                 //cur.bv().stuffClipboard(cur.grabSelection());
986                 return;
987         }
988
989         if (cmd.button() == mouse_button::button2) {
990                 MathArray ar;
991                 asArray(cur.bv().getClipboard(), ar);
992                 cur.clearSelection();
993                 cur.setScreenPos(cmd.x, cmd.y);
994                 cur.insert(ar);
995                 cur.bv().update();
996                 return;
997         }
998
999         if (cmd.button() == mouse_button::button3) {
1000                 // try to dispatch to enclosed insets first
1001                 cur.bv().owner()->getDialogs().show("mathpanel");
1002                 return;
1003         }
1004
1005         cur.undispatched();
1006 }
1007
1008
1009 bool MathNestInset::interpret(LCursor & cur, char c)
1010 {
1011         lyxerr << "interpret 2: '" << c << "'" << endl;
1012         cur.clearTargetX();
1013
1014         // handle macroMode
1015         if (cur.inMacroMode()) {
1016                 string name = cur.macroName();
1017
1018                 /// are we currently typing '#1' or '#2' or...?
1019                 if (name == "\\#") {
1020                         cur.backspace();
1021                         int n = c - '0';
1022                         if (n >= 1 && n <= 9)
1023                                 cur.insert(new MathMacroArgument(n));
1024                         return true;
1025                 }
1026
1027                 if (isalpha(c)) {
1028                         cur.activeMacro()->setName(name + c);
1029                         return true;
1030                 }
1031
1032                 // handle 'special char' macros
1033                 if (name == "\\") {
1034                         // remove the '\\'
1035                         if (c == '\\') {
1036                                 cur.backspace();
1037                                 if (currentMode() == MathInset::TEXT_MODE)
1038                                         cur.niceInsert(createMathInset("textbackslash"));
1039                                 else
1040                                         cur.niceInsert(createMathInset("backslash"));
1041                         } else if (c == '{') {
1042                                 cur.backspace();
1043                                 cur.niceInsert(MathAtom(new MathBraceInset));
1044                         } else if (c == '%') {
1045                                 cur.backspace();
1046                                 cur.niceInsert(MathAtom(new MathCommentInset));
1047                         } else if (c == '#') {
1048                                 BOOST_ASSERT(cur.activeMacro());
1049                                 cur.activeMacro()->setName(name + c);
1050                         } else {
1051                                 cur.backspace();
1052                                 cur.niceInsert(createMathInset(string(1, c)));
1053                         }
1054                         return true;
1055                 }
1056
1057                 // leave macro mode and try again if necessary
1058                 cur.macroModeClose();
1059                 if (c == '{')
1060                         cur.niceInsert(MathAtom(new MathBraceInset));
1061                 else if (c != ' ')
1062                         interpret(cur, c);
1063                 return true;
1064         }
1065
1066         // This is annoying as one has to press <space> far too often.
1067         // Disable it.
1068
1069 #if 0
1070                 // leave autocorrect mode if necessary
1071                 if (autocorrect() && c == ' ') {
1072                         autocorrect() = false;
1073                         return true;
1074                 }
1075 #endif
1076
1077         // just clear selection on pressing the space bar
1078         if (cur.selection() && c == ' ') {
1079                 cur.selection() = false;
1080                 return true;
1081         }
1082
1083         selClearOrDel(cur);
1084
1085         if (c == '\\') {
1086                 //lyxerr << "starting with macro" << endl;
1087                 cur.insert(MathAtom(new MathUnknownInset("\\", false)));
1088                 return true;
1089         }
1090
1091         if (c == '\n') {
1092                 if (currentMode() == MathInset::TEXT_MODE)
1093                         cur.insert(c);
1094                 return true;
1095         }
1096
1097         if (c == ' ') {
1098                 if (currentMode() == MathInset::TEXT_MODE) {
1099                         // insert spaces in text mode,
1100                         // but suppress direct insertion of two spaces in a row
1101                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1102                         // it is better than nothing...
1103                         if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ')
1104                                 cur.insert(c);
1105                         return true;
1106                 }
1107                 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1108                         cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1109                         return true;
1110                 }
1111                 if (cur.popRight())
1112                         return true;
1113                 // if are at the very end, leave the formula
1114                 return cur.pos() != cur.lastpos();
1115         }
1116
1117         if (c == '_') {
1118                 script(cur, false);
1119                 return true;
1120         }
1121
1122         if (c == '^') {
1123                 script(cur, true);
1124                 return true;
1125         }
1126
1127         if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' || c == '%') {
1128                 cur.niceInsert(createMathInset(string(1, c)));
1129                 return true;
1130         }
1131
1132         if (c == '~') {
1133                 cur.niceInsert(createMathInset("sim"));
1134                 return true;
1135         }
1136
1137         // try auto-correction
1138         //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1139         //      return true;
1140
1141         // no special circumstances, so insert the character without any fuss
1142         cur.insert(c);
1143         cur.autocorrect() = true;
1144         return true;
1145 }
1146
1147
1148 bool MathNestInset::script(LCursor & cur, bool up)
1149 {
1150         // Hack to get \^ and \_ working
1151         lyxerr << "handling script: up: " << up << endl;
1152         if (cur.inMacroMode() && cur.macroName() == "\\") {
1153                 if (up)
1154                         cur.niceInsert(createMathInset("mathcircumflex"));
1155                 else
1156                         interpret(cur, '_');
1157                 return true;
1158         }
1159
1160         cur.macroModeClose();
1161         string safe = grabAndEraseSelection(cur);
1162         if (asScriptInset() && cur.idx() == 0) {
1163                 // we are in a nucleus of a script inset, move to _our_ script
1164                 MathScriptInset * inset = asScriptInset();
1165                 lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
1166                 inset->ensure(up);
1167                 cur.idx() = inset->idxOfScript(up);
1168                 cur.pos() = 0;
1169         } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1170                 --cur.pos();
1171                 MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
1172                 cur.push(*inset);
1173                 cur.idx() = inset->idxOfScript(up);
1174                 cur.pos() = cur.lastpos();
1175         } else {
1176                 // convert the thing to our left to a scriptinset or create a new
1177                 // one if in the very first position of the array
1178                 if (cur.pos() == 0) {
1179                         lyxerr << "new scriptinset" << endl;
1180                         cur.insert(new MathScriptInset(up));
1181                 } else {
1182                         lyxerr << "converting prev atom " << endl;
1183                         cur.prevAtom() = MathAtom(new MathScriptInset(cur.prevAtom(), up));
1184                 }
1185                 --cur.pos();
1186                 MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
1187                 cur.push(*inset);
1188                 cur.idx() = 1;
1189                 cur.pos() = 0;
1190         }
1191         lyxerr << "pasting 1: safe:\n" << safe << endl;
1192         cur.paste(safe);
1193         cur.resetAnchor();
1194         lyxerr << "pasting 2: safe:\n" << safe << endl;
1195         return true;
1196 }