]> git.lyx.org Git - lyx.git/blob - src/mathed/math_nestinset.C
rename priv_dispatch to doDispatch
[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::doDispatch(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);
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);
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                 // ignore math-mode on when already in math mode
688                 if (currentMode() == InsetBase::MATH_MODE && cmd.argument == "on")
689                         break;
690                 cur.macroModeClose();
691                 selClearOrDel(cur);
692                 cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
693                 cur.posLeft();
694                 cur.pushLeft(*cur.nextInset());
695 #else
696                 if (currentMode() == InsetBase::TEXT_MODE) {
697                         cur.niceInsert(MathAtom(new MathHullInset("simple")));
698                         cur.message(_("create new math text environment ($...$)"));
699                 } else {
700                         handleFont(cur, cmd.argument, "textrm");
701                         cur.message(_("entered math text mode (textrm)"));
702                 }
703 #endif
704                 break;
705
706         case LFUN_MATH_SIZE:
707 #if 0
708                 recordUndo(cur);
709                 cur.setSize(arg);
710 #endif
711                 break;
712
713         case LFUN_INSERT_MATRIX: {
714                 recordUndo(cur, Undo::ATOMIC);
715                 unsigned int m = 1;
716                 unsigned int n = 1;
717                 string v_align;
718                 string h_align;
719                 istringstream is(cmd.argument);
720                 is >> m >> n >> v_align >> h_align;
721                 if (m < 1)
722                         m = 1;
723                 if (n < 1)
724                         n = 1;
725                 v_align += 'c';
726                 cur.niceInsert(
727                         MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
728                 break;
729         }
730
731         case LFUN_MATH_DELIM: {
732                 lyxerr << "MathNestInset::LFUN_MATH_DELIM" << endl;
733                 string ls;
734                 string rs = lyx::support::split(cmd.argument, ls, ' ');
735                 // Reasonable default values
736                 if (ls.empty())
737                         ls = '(';
738                 if (rs.empty())
739                         rs = ')';
740                 recordUndo(cur, Undo::ATOMIC);
741                 cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
742                 break;
743         }
744
745         case LFUN_SPACE_INSERT:
746         case LFUN_MATH_SPACE:
747                 recordUndo(cur, Undo::ATOMIC);
748                 cur.insert(MathAtom(new MathSpaceInset(",")));
749                 break;
750
751         case LFUN_INSET_ERT:
752                 // interpret this as if a backslash was typed
753                 recordUndo(cur, Undo::ATOMIC);
754                 interpret(cur, '\\');
755                 break;
756
757 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
758 // handling such that "self-insert" works on "arbitrary stuff" too, and
759 // math-insert only handles special math things like "matrix".
760         case LFUN_INSERT_MATH:
761                 recordUndo(cur, Undo::ATOMIC);
762                 cur.niceInsert(cmd.argument);
763                 break;
764
765         case LFUN_DIALOG_SHOW_NEW_INSET: {
766                 string const & name = cmd.argument;
767                 string data;
768 #if 0
769                 if (name == "ref") {
770                         RefInset tmp(name);
771                         data = tmp.createDialogStr(name);
772                 }
773 #endif
774                 cur.bv().owner()->getDialogs().show(name, data, 0);
775                 break;
776         }
777
778         case LFUN_INSET_APPLY: {
779                 string const name = cmd.getArg(0);
780                 InsetBase * base = cur.bv().owner()->getDialogs().getOpenInset(name);
781
782                 if (base) {
783                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
784                         base->dispatch(cur, fr);
785                         break;
786                 }
787                 MathArray ar;
788                 if (createMathInset_fromDialogStr(cmd.argument, ar)) {
789                         cur.insert(ar);
790                         break;
791                 }
792                 cur.undispatched();
793                 break;
794         }
795
796         default:
797                 MathDimInset::doDispatch(cur, cmd);
798                 break;
799         }
800 }
801
802
803 bool MathNestInset::getStatus(LCursor & /*cur*/, FuncRequest const & cmd,
804                 FuncStatus & flag) const
805 {
806         // the font related toggles
807         //string tc = "mathnormal";
808         bool ret = true;
809         switch (cmd.action) {
810 #if 0
811         case LFUN_TABULAR_FEATURE:
812                 // FIXME: check temporarily disabled
813                 // valign code
814                 char align = mathcursor::valign();
815                 if (align == '\0') {
816                         enable = false;
817                         break;
818                 }
819                 if (cmd.argument.empty()) {
820                         flag.clear();
821                         break;
822                 }
823                 if (!contains("tcb", cmd.argument[0])) {
824                         enable = false;
825                         break;
826                 }
827                 flag.setOnOff(cmd.argument[0] == align);
828                 break;
829         case LFUN_BOLD:
830                 flag.setOnOff(tc == "mathbf");
831                 break;
832         case LFUN_SANS:
833                 flag.setOnOff(tc == "mathsf");
834                 break;
835         case LFUN_EMPH:
836                 flag.setOnOff(tc == "mathcal");
837                 break;
838         case LFUN_ROMAN:
839                 flag.setOnOff(tc == "mathrm");
840                 break;
841         case LFUN_CODE:
842                 flag.setOnOff(tc == "mathtt");
843                 break;
844         case LFUN_NOUN:
845                 flag.setOnOff(tc == "mathbb");
846                 break;
847         case LFUN_DEFAULT:
848                 flag.setOnOff(tc == "mathnormal");
849                 break;
850 #endif
851         case LFUN_MATH_MUTATE:
852                 //flag.setOnOff(mathcursor::formula()->hullType() == cmd.argument);
853                 flag.setOnOff(false);
854                 break;
855
856         // we just need to be in math mode to enable that
857         case LFUN_MATH_SIZE:
858         case LFUN_MATH_SPACE:
859         case LFUN_MATH_LIMITS:
860         case LFUN_MATH_NONUMBER:
861         case LFUN_MATH_NUMBER:
862         case LFUN_MATH_EXTERN:
863                 flag.enabled(true);
864                 break;
865
866         default:
867                 ret = false;
868                 break;
869         }
870         return ret;
871 }
872
873
874 void MathNestInset::edit(LCursor & cur, bool left)
875 {
876         cur.push(*this);
877         cur.idx() = left ? 0 : cur.lastidx();
878         cur.pos() = left ? 0 : cur.lastpos();
879         cur.resetAnchor();
880         lyxerr << "MathNestInset::edit, cur:\n" << cur << endl;
881 }
882
883
884 InsetBase * MathNestInset::editXY(LCursor & cur, int x, int y) const
885 {
886         int idx_min = 0;
887         int dist_min = 1000000;
888         for (idx_type i = 0; i < nargs(); ++i) {
889                 int d = cell(i).dist(x, y);
890                 if (d < dist_min) {
891                         dist_min = d;
892                         idx_min = i;
893                 }
894         }
895         MathArray const & ar = cell(idx_min);
896         cur.push(const_cast<MathNestInset&>(*this));
897         cur.idx() = idx_min;
898         cur.pos() = ar.x2pos(x - ar.xo());
899         lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
900         if (dist_min == 0) {
901                 // hit inside cell
902                 for (pos_type i = 0, n = ar.size(); i < n; ++i)
903                         if (ar[i]->covers(x, y))
904                                 return ar[i].nucleus()->editXY(cur, x, y);
905         }
906         return const_cast<MathNestInset*>(this);
907 }
908
909
910 void MathNestInset::lfunMousePress(LCursor & cur, FuncRequest & cmd)
911 {
912         lyxerr << "lfunMousePress: buttons: " << cmd.button() << endl;
913         if (cmd.button() == mouse_button::button1) {
914                 first_x = cmd.x;
915                 first_y = cmd.y;
916                 lyxerr << "lfunMousePress: setting cursor to: " << cur << endl;
917                 cur.resetAnchor();
918                 cur.bv().cursor() = cur;
919         }
920
921         if (cmd.button() == mouse_button::button2) {
922                 cur.dispatch(FuncRequest(LFUN_PASTESELECTION));
923         }
924 }
925
926
927 void MathNestInset::lfunMouseMotion(LCursor & cur, FuncRequest & cmd)
928 {
929         // only select with button 1
930         if (cmd.button() == mouse_button::button1) {
931                 LCursor & bvcur = cur.bv().cursor();
932                 if (abs(cmd.x - first_x) + abs(cmd.y - first_y) > 4
933                     && bvcur.anchor_.hasPart(cur)) {
934                         first_x = cmd.x;
935                         first_y = cmd.y;
936
937                         bvcur.setCursor(cur);
938                         bvcur.selection() = true;
939                 }
940         }
941 }
942
943
944 void MathNestInset::lfunMouseRelease(LCursor & cur, FuncRequest & cmd)
945 {
946         lyxerr << "lfunMouseRelease: buttons: " << cmd.button() << endl;
947
948         if (cmd.button() == mouse_button::button1) {
949                 //cur.bv().stuffClipboard(cur.grabSelection());
950                 return;
951         }
952
953         if (cmd.button() == mouse_button::button2) {
954                 MathArray ar;
955                 asArray(cur.bv().getClipboard(), ar);
956                 cur.clearSelection();
957                 cur.setScreenPos(cmd.x, cmd.y);
958                 cur.insert(ar);
959                 cur.bv().update();
960                 return;
961         }
962
963         if (cmd.button() == mouse_button::button3) {
964                 // try to dispatch to enclosed insets first
965                 cur.bv().owner()->getDialogs().show("mathpanel");
966                 return;
967         }
968
969         cur.undispatched();
970 }
971
972
973 bool MathNestInset::interpret(LCursor & cur, char c)
974 {
975         lyxerr << "interpret 2: '" << c << "'" << endl;
976         cur.clearTargetX();
977
978         // handle macroMode
979         if (cur.inMacroMode()) {
980                 string name = cur.macroName();
981
982                 /// are we currently typing '#1' or '#2' or...?
983                 if (name == "\\#") {
984                         cur.backspace();
985                         int n = c - '0';
986                         if (n >= 1 && n <= 9)
987                                 cur.insert(new MathMacroArgument(n));
988                         return true;
989                 }
990
991                 if (isalpha(c)) {
992                         cur.activeMacro()->setName(name + c);
993                         return true;
994                 }
995
996                 // handle 'special char' macros
997                 if (name == "\\") {
998                         // remove the '\\'
999                         if (c == '\\') {
1000                                 cur.backspace();
1001                                 if (currentMode() == MathInset::TEXT_MODE)
1002                                         cur.niceInsert(createMathInset("textbackslash"));
1003                                 else
1004                                         cur.niceInsert(createMathInset("backslash"));
1005                         } else if (c == '{') {
1006                                 cur.backspace();
1007                                 cur.niceInsert(MathAtom(new MathBraceInset));
1008                         } else if (c == '%') {
1009                                 cur.backspace();
1010                                 cur.niceInsert(MathAtom(new MathCommentInset));
1011                         } else if (c == '#') {
1012                                 BOOST_ASSERT(cur.activeMacro());
1013                                 cur.activeMacro()->setName(name + c);
1014                         } else {
1015                                 cur.backspace();
1016                                 cur.niceInsert(createMathInset(string(1, c)));
1017                         }
1018                         return true;
1019                 }
1020
1021                 // leave macro mode and try again if necessary
1022                 cur.macroModeClose();
1023                 if (c == '{')
1024                         cur.niceInsert(MathAtom(new MathBraceInset));
1025                 else if (c != ' ')
1026                         interpret(cur, c);
1027                 return true;
1028         }
1029
1030         // This is annoying as one has to press <space> far too often.
1031         // Disable it.
1032
1033 #if 0
1034                 // leave autocorrect mode if necessary
1035                 if (autocorrect() && c == ' ') {
1036                         autocorrect() = false;
1037                         return true;
1038                 }
1039 #endif
1040
1041         // just clear selection on pressing the space bar
1042         if (cur.selection() && c == ' ') {
1043                 cur.selection() = false;
1044                 return true;
1045         }
1046
1047         selClearOrDel(cur);
1048
1049         if (c == '\\') {
1050                 //lyxerr << "starting with macro" << endl;
1051                 cur.insert(MathAtom(new MathUnknownInset("\\", false)));
1052                 return true;
1053         }
1054
1055         if (c == '\n') {
1056                 if (currentMode() == MathInset::TEXT_MODE)
1057                         cur.insert(c);
1058                 return true;
1059         }
1060
1061         if (c == ' ') {
1062                 if (currentMode() == MathInset::TEXT_MODE) {
1063                         // insert spaces in text mode,
1064                         // but suppress direct insertion of two spaces in a row
1065                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1066                         // it is better than nothing...
1067                         if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ')
1068                                 cur.insert(c);
1069                         return true;
1070                 }
1071                 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1072                         cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1073                         return true;
1074                 }
1075                 if (cur.popRight())
1076                         return true;
1077                 // if are at the very end, leave the formula
1078                 return cur.pos() != cur.lastpos();
1079         }
1080
1081         if (c == '_') {
1082                 script(cur, false);
1083                 return true;
1084         }
1085
1086         if (c == '^') {
1087                 script(cur, true);
1088                 return true;
1089         }
1090
1091         if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' || c == '%') {
1092                 cur.niceInsert(createMathInset(string(1, c)));
1093                 return true;
1094         }
1095
1096         if (c == '~') {
1097                 cur.niceInsert(createMathInset("sim"));
1098                 return true;
1099         }
1100
1101         // try auto-correction
1102         //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1103         //      return true;
1104
1105         // no special circumstances, so insert the character without any fuss
1106         cur.insert(c);
1107         cur.autocorrect() = true;
1108         return true;
1109 }
1110
1111
1112 bool MathNestInset::script(LCursor & cur, bool up)
1113 {
1114         // Hack to get \^ and \_ working
1115         lyxerr << "handling script: up: " << up << endl;
1116         if (cur.inMacroMode() && cur.macroName() == "\\") {
1117                 if (up)
1118                         cur.niceInsert(createMathInset("mathcircumflex"));
1119                 else
1120                         interpret(cur, '_');
1121                 return true;
1122         }
1123
1124         cur.macroModeClose();
1125         string safe = grabAndEraseSelection(cur);
1126         if (asScriptInset() && cur.idx() == 0) {
1127                 // we are in a nucleus of a script inset, move to _our_ script
1128                 MathScriptInset * inset = asScriptInset();
1129                 lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
1130                 inset->ensure(up);
1131                 cur.idx() = inset->idxOfScript(up);
1132                 cur.pos() = 0;
1133         } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1134                 --cur.pos();
1135                 MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
1136                 cur.push(*inset);
1137                 cur.idx() = inset->idxOfScript(up);
1138                 cur.pos() = cur.lastpos();
1139         } else {
1140                 // convert the thing to our left to a scriptinset or create a new
1141                 // one if in the very first position of the array
1142                 if (cur.pos() == 0) {
1143                         lyxerr << "new scriptinset" << endl;
1144                         cur.insert(new MathScriptInset(up));
1145                 } else {
1146                         lyxerr << "converting prev atom " << endl;
1147                         cur.prevAtom() = MathAtom(new MathScriptInset(cur.prevAtom(), up));
1148                 }
1149                 --cur.pos();
1150                 MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
1151                 cur.push(*inset);
1152                 cur.idx() = 1;
1153                 cur.pos() = 0;
1154         }
1155         lyxerr << "pasting 1: safe:\n" << safe << endl;
1156         cur.paste(safe);
1157         cur.resetAnchor();
1158         lyxerr << "pasting 2: safe:\n" << safe << endl;
1159         return true;
1160 }