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