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