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