]> git.lyx.org Git - lyx.git/blob - src/mathed/math_nestinset.C
3d54c3f35b9f77f7021e34e2b1b8647cd9959bc8
[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                         safe_ = grabAndEraseSelection(cur);
716                         script(cur, true);
717                 }
718                 break;
719
720         case LFUN_UMLAUT:
721         case LFUN_ACUTE:
722         case LFUN_GRAVE:
723         case LFUN_BREVE:
724         case LFUN_DOT:
725         case LFUN_MACRON:
726         case LFUN_CARON:
727         case LFUN_TILDE:
728         case LFUN_CEDILLA:
729         case LFUN_CIRCLE:
730         case LFUN_UNDERDOT:
731         case LFUN_TIE:
732         case LFUN_OGONEK:
733         case LFUN_HUNG_UMLAUT:
734                 break;
735
736         //  Math fonts
737         case LFUN_FREEFONT_APPLY:
738         case LFUN_FREEFONT_UPDATE:
739                 handleFont2(cur, cmd.argument);
740                 break;
741
742         case LFUN_BOLD:
743                 if (currentMode() == TEXT_MODE)
744                         handleFont(cur, cmd.argument, "textbf");
745                 else
746                         handleFont(cur, cmd.argument, "mathbf");
747                 break;
748         case LFUN_SANS:
749                 if (currentMode() == TEXT_MODE)
750                         handleFont(cur, cmd.argument, "textsf");
751                 else
752                         handleFont(cur, cmd.argument, "mathsf");
753                 break;
754         case LFUN_EMPH:
755                 if (currentMode() == TEXT_MODE)
756                         handleFont(cur, cmd.argument, "emph");
757                 else
758                         handleFont(cur, cmd.argument, "mathcal");
759                 break;
760         case LFUN_ROMAN:
761                 if (currentMode() == TEXT_MODE)
762                         handleFont(cur, cmd.argument, "textrm");
763                 else
764                         handleFont(cur, cmd.argument, "mathrm");
765                 break;
766         case LFUN_CODE:
767                 if (currentMode() == TEXT_MODE)
768                         handleFont(cur, cmd.argument, "texttt");
769                 else
770                         handleFont(cur, cmd.argument, "mathtt");
771                 break;
772         case LFUN_FRAK:
773                 handleFont(cur, cmd.argument, "mathfrak");
774                 break;
775         case LFUN_ITAL:
776                 if (currentMode() == TEXT_MODE)
777                         handleFont(cur, cmd.argument, "textit");
778                 else
779                         handleFont(cur, cmd.argument, "mathit");
780                 break;
781         case LFUN_NOUN:
782                 if (currentMode() == TEXT_MODE)
783                         // FIXME: should be "noun"
784                         handleFont(cur, cmd.argument, "textsc");
785                 else
786                         handleFont(cur, cmd.argument, "mathbb");
787                 break;
788         //case LFUN_FREEFONT_APPLY:
789                 handleFont(cur, cmd.argument, "textrm");
790                 break;
791         case LFUN_DEFAULT:
792                 handleFont(cur, cmd.argument, "textnormal");
793                 break;
794
795         case LFUN_MATH_MODE:
796 #if 1
797                 // ignore math-mode on when already in math mode
798                 if (currentMode() == InsetBase::MATH_MODE && cmd.argument == "on")
799                         break;
800                 cur.macroModeClose();
801                 selClearOrDel(cur);
802                 //cur.plainInsert(MathAtom(new MathMBoxInset(cur.bv())));
803                 cur.plainInsert(MathAtom(new MathBoxInset("mbox")));
804                 cur.posLeft();
805                 cur.pushLeft(*cur.nextInset());
806 #else
807                 if (currentMode() == InsetBase::TEXT_MODE) {
808                         cur.niceInsert(MathAtom(new MathHullInset("simple")));
809                         cur.message(_("create new math text environment ($...$)"));
810                 } else {
811                         handleFont(cur, cmd.argument, "textrm");
812                         cur.message(_("entered math text mode (textrm)"));
813                 }
814 #endif
815                 break;
816
817         case LFUN_MATH_SIZE:
818 #if 0
819                 recordUndo(cur);
820                 cur.setSize(arg);
821 #endif
822                 break;
823
824         case LFUN_INSERT_MATRIX: {
825                 recordUndo(cur, Undo::ATOMIC);
826                 unsigned int m = 1;
827                 unsigned int n = 1;
828                 string v_align;
829                 string h_align;
830                 istringstream is(cmd.argument);
831                 is >> m >> n >> v_align >> h_align;
832                 if (m < 1)
833                         m = 1;
834                 if (n < 1)
835                         n = 1;
836                 v_align += 'c';
837                 cur.niceInsert(
838                         MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
839                 break;
840         }
841
842         case LFUN_MATH_DELIM: {
843                 lyxerr << "MathNestInset::LFUN_MATH_DELIM" << endl;
844                 string ls;
845                 string rs = lyx::support::split(cmd.argument, ls, ' ');
846                 // Reasonable default values
847                 if (ls.empty())
848                         ls = '(';
849                 if (rs.empty())
850                         rs = ')';
851                 recordUndo(cur, Undo::ATOMIC);
852                 // Don't do this with multi-cell selections
853                 if (cur.selBegin().idx() == cur.selEnd().idx())
854                         cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
855                 break;
856         }
857
858         case LFUN_SPACE_INSERT:
859         case LFUN_MATH_SPACE:
860                 recordUndo(cur, Undo::ATOMIC);
861                 cur.insert(MathAtom(new MathSpaceInset(",")));
862                 break;
863
864         case LFUN_INSET_ERT:
865                 // interpret this as if a backslash was typed
866                 recordUndo(cur, Undo::ATOMIC);
867                 interpret(cur, '\\');
868                 break;
869
870         case LFUN_SUBSCRIPT:
871                 // interpret this as if a _ was typed
872                 recordUndo(cur, Undo::ATOMIC);
873                 interpret(cur, '_');
874                 break;
875
876         case LFUN_SUPERSCRIPT:
877                 // interpret this as if a ^ was typed
878                 recordUndo(cur, Undo::ATOMIC);
879                 interpret(cur, '^');
880                 break;
881
882 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
883 // handling such that "self-insert" works on "arbitrary stuff" too, and
884 // math-insert only handles special math things like "matrix".
885         case LFUN_INSERT_MATH: {
886                 recordUndo(cur, Undo::ATOMIC);
887                 if (cmd.argument == "^" || cmd.argument == "_") {
888                         interpret(cur, cmd.argument[0]);
889                 } else
890                         cur.niceInsert(cmd.argument);
891                 break;
892                 }
893
894         case LFUN_DIALOG_SHOW_NEW_INSET: {
895                 string const & name = cmd.argument;
896                 string data;
897                 if (name == "ref") {
898                         RefInset tmp(name);
899                         data = tmp.createDialogStr(name);
900                 }
901                 cur.bv().owner()->getDialogs().show(name, data, 0);
902                 break;
903         }
904
905         default:
906                 MathDimInset::doDispatch(cur, cmd);
907                 break;
908         }
909 }
910
911
912 bool MathNestInset::getStatus(LCursor & /*cur*/, FuncRequest const & cmd,
913                 FuncStatus & flag) const
914 {
915         // the font related toggles
916         //string tc = "mathnormal";
917         bool ret = true;
918         string const arg = cmd.argument;
919         switch (cmd.action) {
920         case LFUN_TABULAR_FEATURE:
921                 flag.enabled(false);
922                 break;
923 #if 0
924         case LFUN_TABULAR_FEATURE:
925                 // FIXME: check temporarily disabled
926                 // valign code
927                 char align = mathcursor::valign();
928                 if (align == '\0') {
929                         enable = false;
930                         break;
931                 }
932                 if (cmd.argument.empty()) {
933                         flag.clear();
934                         break;
935                 }
936                 if (!contains("tcb", cmd.argument[0])) {
937                         enable = false;
938                         break;
939                 }
940                 flag.setOnOff(cmd.argument[0] == align);
941                 break;
942 #endif
943         /// We have to handle them since 1.4 blocks all unhandled actions
944         case LFUN_ITAL:
945         case LFUN_BOLD:
946         case LFUN_SANS:
947         case LFUN_EMPH:
948         case LFUN_CODE:
949         case LFUN_NOUN:
950         case LFUN_ROMAN:
951         case LFUN_DEFAULT:
952                 flag.enabled(true);
953                 break;
954         case LFUN_MATH_MUTATE:
955                 //flag.setOnOff(mathcursor::formula()->hullType() == cmd.argument);
956                 flag.setOnOff(false);
957                 break;
958
959         // we just need to be in math mode to enable that
960         case LFUN_MATH_SIZE:
961         case LFUN_MATH_SPACE:
962         case LFUN_MATH_LIMITS:
963         case LFUN_MATH_NONUMBER:
964         case LFUN_MATH_NUMBER:
965         case LFUN_MATH_EXTERN:
966                 flag.enabled(true);
967                 break;
968
969         case LFUN_FRAK:
970                 flag.enabled(currentMode() != TEXT_MODE);
971                 break;
972
973         case LFUN_INSERT_MATH: {
974                 bool const textarg =
975                         arg == "\\textbf"   || arg == "\\textsf" ||
976                         arg == "\\textrm"   || arg == "\\textmd" ||
977                         arg == "\\textit"   || arg == "\\textsc" ||
978                         arg == "\\textsl"   || arg == "\\textup" ||
979                         arg == "\\texttt"   || arg == "\\textbb" ||
980                         arg == "\\textnormal";
981                 flag.enabled(currentMode() != TEXT_MODE || textarg);
982                 break;
983         }
984
985         case LFUN_INSERT_MATRIX:
986                 flag.enabled(currentMode() == MATH_MODE);
987                 break;
988         default:
989                 ret = false;
990                 break;
991         }
992         return ret;
993 }
994
995
996 void MathNestInset::edit(LCursor & cur, bool left)
997 {
998         cur.push(*this);
999         cur.idx() = left ? 0 : cur.lastidx();
1000         cur.pos() = left ? 0 : cur.lastpos();
1001         cur.resetAnchor();
1002         //lyxerr << "MathNestInset::edit, cur:\n" << cur << endl;
1003 }
1004
1005
1006 InsetBase * MathNestInset::editXY(LCursor & cur, int x, int y)
1007 {
1008         int idx_min = 0;
1009         int dist_min = 1000000;
1010         for (idx_type i = 0, n = nargs(); i != n; ++i) {
1011                 int const d = cell(i).dist(x, y);
1012                 if (d < dist_min) {
1013                         dist_min = d;
1014                         idx_min = i;
1015                 }
1016         }
1017         MathArray & ar = cell(idx_min);
1018         cur.push(*this);
1019         cur.idx() = idx_min;
1020         cur.pos() = ar.x2pos(x - ar.xo());
1021         //lyxerr << "found cell : " << idx_min << " pos: " << cur.pos() << endl;
1022         if (dist_min == 0) {
1023                 // hit inside cell
1024                 for (pos_type i = 0, n = ar.size(); i < n; ++i)
1025                         if (ar[i]->covers(x, y))
1026                                 return ar[i].nucleus()->editXY(cur, x, y);
1027         }
1028         return this;
1029 }
1030
1031
1032 void MathNestInset::lfunMousePress(LCursor & cur, FuncRequest & cmd)
1033 {
1034         //lyxerr << "## lfunMousePress: buttons: " << cmd.button() << endl;
1035         if (cmd.button() == mouse_button::button1) {
1036                 //lyxerr << "## lfunMousePress: setting cursor to: " << cur << endl;
1037                 cur.bv().mouseSetCursor(cur);
1038         }
1039
1040         if (cmd.button() == mouse_button::button2) {
1041                 cur.dispatch(FuncRequest(LFUN_PASTESELECTION));
1042         }
1043 }
1044
1045
1046 void MathNestInset::lfunMouseMotion(LCursor & cur, FuncRequest & cmd)
1047 {
1048         // only select with button 1
1049         if (cmd.button() == mouse_button::button1) {
1050                 LCursor & bvcur = cur.bv().cursor();
1051                 if (bvcur.anchor_.hasPart(cur)) {
1052                         //lyxerr << "## lfunMouseMotion: cursor: " << cur << endl;
1053                         bvcur.setCursor(cur);
1054                         bvcur.selection() = true;
1055                         //lyxerr << "MOTION " << bvcur << endl;
1056                 }
1057                 else {
1058                         cur.undispatched();
1059                 }
1060         }
1061 }
1062
1063
1064 void MathNestInset::lfunMouseRelease(LCursor & cur, FuncRequest & cmd)
1065 {
1066         //lyxerr << "## lfunMouseRelease: buttons: " << cmd.button() << endl;
1067
1068         if (cmd.button() == mouse_button::button1) {
1069                 //cur.bv().stuffClipboard(cur.grabSelection());
1070                 return;
1071         }
1072
1073         if (cmd.button() == mouse_button::button2) {
1074                 MathArray ar;
1075                 asArray(cur.bv().getClipboard(), ar);
1076                 cur.clearSelection();
1077                 cur.setScreenPos(cmd.x, cmd.y);
1078                 cur.insert(ar);
1079                 cur.bv().update();
1080                 return;
1081         }
1082
1083         if (cmd.button() == mouse_button::button3) {
1084                 // try to dispatch to enclosed insets first
1085                 cur.bv().owner()->getDialogs().show("mathpanel");
1086                 return;
1087         }
1088
1089         cur.undispatched();
1090 }
1091
1092
1093 bool MathNestInset::interpret(LCursor & cur, char c)
1094 {
1095         //lyxerr << "interpret 2: '" << c << "'" << endl;
1096         if (c == '^' || c == '_')
1097                 safe_ = grabAndEraseSelection(cur);
1098
1099         cur.clearTargetX();
1100
1101         // handle macroMode
1102         if (cur.inMacroMode()) {
1103                 string name = cur.macroName();
1104
1105                 /// are we currently typing '#1' or '#2' or...?
1106                 if (name == "\\#") {
1107                         cur.backspace();
1108                         int n = c - '0';
1109                         if (n >= 1 && n <= 9)
1110                                 cur.insert(new MathMacroArgument(n));
1111                         return true;
1112                 }
1113
1114                 if (isalpha(c)) {
1115                         cur.activeMacro()->setName(name + c);
1116                         return true;
1117                 }
1118
1119                 // handle 'special char' macros
1120                 if (name == "\\") {
1121                         // remove the '\\'
1122                         if (c == '\\') {
1123                                 cur.backspace();
1124                                 if (currentMode() == MathInset::TEXT_MODE)
1125                                         cur.niceInsert(createMathInset("textbackslash"));
1126                                 else
1127                                         cur.niceInsert(createMathInset("backslash"));
1128                         } else if (c == '{') {
1129                                 cur.backspace();
1130                                 cur.niceInsert(MathAtom(new MathBraceInset));
1131                         } else if (c == '%') {
1132                                 cur.backspace();
1133                                 cur.niceInsert(MathAtom(new MathCommentInset));
1134                         } else if (c == '#') {
1135                                 BOOST_ASSERT(cur.activeMacro());
1136                                 cur.activeMacro()->setName(name + c);
1137                         } else {
1138                                 cur.backspace();
1139                                 cur.niceInsert(createMathInset(string(1, c)));
1140                         }
1141                         return true;
1142                 }
1143
1144                 // leave macro mode and try again if necessary
1145                 cur.macroModeClose();
1146                 if (c == '{')
1147                         cur.niceInsert(MathAtom(new MathBraceInset));
1148                 else if (c != ' ')
1149                         interpret(cur, c);
1150                 return true;
1151         }
1152
1153         // This is annoying as one has to press <space> far too often.
1154         // Disable it.
1155
1156 #if 0
1157                 // leave autocorrect mode if necessary
1158                 if (autocorrect() && c == ' ') {
1159                         autocorrect() = false;
1160                         return true;
1161                 }
1162 #endif
1163
1164         // just clear selection on pressing the space bar
1165         if (cur.selection() && c == ' ') {
1166                 cur.selection() = false;
1167                 return true;
1168         }
1169
1170         selClearOrDel(cur);
1171
1172         if (c == '\\') {
1173                 //lyxerr << "starting with macro" << endl;
1174                 cur.insert(MathAtom(new MathUnknownInset("\\", false)));
1175                 return true;
1176         }
1177
1178         if (c == '\n') {
1179                 if (currentMode() == MathInset::TEXT_MODE)
1180                         cur.insert(c);
1181                 return true;
1182         }
1183
1184         if (c == ' ') {
1185                 if (currentMode() == MathInset::TEXT_MODE) {
1186                         // insert spaces in text mode,
1187                         // but suppress direct insertion of two spaces in a row
1188                         // the still allows typing  '<space>a<space>' and deleting the 'a', but
1189                         // it is better than nothing...
1190                         if (!cur.pos() != 0 || cur.prevAtom()->getChar() != ' ')
1191                                 cur.insert(c);
1192                         return true;
1193                 }
1194                 if (cur.pos() != 0 && cur.prevAtom()->asSpaceInset()) {
1195                         cur.prevAtom().nucleus()->asSpaceInset()->incSpace();
1196                         return true;
1197                 }
1198                 if (cur.popRight())
1199                         return true;
1200                 // if are at the very end, leave the formula
1201                 return cur.pos() != cur.lastpos();
1202         }
1203
1204         // These shouldn't work in text mode:
1205         if (currentMode() != MathInset::TEXT_MODE) {
1206                 if (c == '_') {
1207                         script(cur, false);
1208                         return true;
1209                 }
1210                 if (c == '^') {
1211                         script(cur, true);
1212                         return true;
1213                 }
1214                 if (c == '~') {
1215                         cur.niceInsert(createMathInset("sim"));
1216                         return true;
1217                 }
1218         }
1219
1220         if (c == '{' || c == '}' || c == '&' || c == '$' || c == '#' || c == '%'
1221       || c == '_' || c == '^') {
1222                 cur.niceInsert(createMathInset(string(1, c)));
1223                 return true;
1224         }
1225
1226
1227         // try auto-correction
1228         //if (autocorrect() && hasPrevAtom() && math_autocorrect(prevAtom(), c))
1229         //      return true;
1230
1231         // no special circumstances, so insert the character without any fuss
1232         cur.insert(c);
1233         cur.autocorrect() = true;
1234         return true;
1235 }
1236
1237
1238 bool MathNestInset::script(LCursor & cur, bool up)
1239 {
1240         // Hack to get \^ and \_ working
1241         lyxerr << "handling script: up: " << up << endl;
1242         if (cur.inMacroMode() && cur.macroName() == "\\") {
1243                 if (up)
1244                         cur.niceInsert(createMathInset("mathcircumflex"));
1245                 else
1246                         interpret(cur, '_');
1247                 return true;
1248         }
1249
1250         cur.macroModeClose();
1251         if (asScriptInset() && cur.idx() == 0) {
1252                 // we are in a nucleus of a script inset, move to _our_ script
1253                 MathScriptInset * inset = asScriptInset();
1254                 lyxerr << " going to cell " << inset->idxOfScript(up) << endl;
1255                 inset->ensure(up);
1256                 cur.idx() = inset->idxOfScript(up);
1257                 cur.pos() = 0;
1258         } else if (cur.pos() != 0 && cur.prevAtom()->asScriptInset()) {
1259                 --cur.pos();
1260                 MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
1261                 cur.push(*inset);
1262                 inset->ensure(up);
1263                 cur.idx() = inset->idxOfScript(up);
1264                 cur.pos() = cur.lastpos();
1265         } else {
1266                 // convert the thing to our left to a scriptinset or create a new
1267                 // one if in the very first position of the array
1268                 if (cur.pos() == 0) {
1269                         //lyxerr << "new scriptinset" << endl;
1270                         cur.insert(new MathScriptInset(up));
1271                 } else {
1272                         //lyxerr << "converting prev atom " << endl;
1273                         cur.prevAtom() = MathAtom(new MathScriptInset(cur.prevAtom(), up));
1274                 }
1275                 --cur.pos();
1276                 MathScriptInset * inset = cur.nextAtom().nucleus()->asScriptInset();
1277                 cur.push(*inset);
1278                 cur.idx() = 1;
1279                 cur.pos() = 0;
1280         }
1281         //lyxerr << "inserting 1: safe:\n" << safe_ << endl;
1282         cur.niceInsert(safe_);
1283         cur.resetAnchor();
1284         //lyxerr << "inserting 2: safe:\n" << safe_ << endl;
1285         return true;
1286 }