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