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