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