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