]> git.lyx.org Git - lyx.git/blob - src/mathed/math_nestinset.C
more IU
[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 "BufferView.h"
16 #include "LColor.h"
17 #include "cursor.h"
18 #include "debug.h"
19 #include "dispatchresult.h"
20 #include "funcrequest.h"
21 #include "math_data.h"
22 #include "math_mathmlstream.h"
23 #include "math_parser.h"
24 #include "undo.h"
25
26 #include "frontends/Painter.h"
27
28
29 MathNestInset::MathNestInset(idx_type nargs)
30         : cells_(nargs), lock_(false)
31 {}
32
33
34 MathInset::idx_type MathNestInset::nargs() const
35 {
36         return cells_.size();
37 }
38
39
40 MathArray & MathNestInset::cell(idx_type i)
41 {
42         return cells_[i];
43 }
44
45
46 MathArray const & MathNestInset::cell(idx_type i) const
47 {
48         return cells_[i];
49 }
50
51
52 void MathNestInset::getScreenPos(idx_type idx, pos_type pos, int & x, int & y) const
53 {
54         MathArray const & ar = cell(idx);
55         x = ar.xo() + ar.pos2x(pos);
56         y = ar.yo();
57         // move cursor visually into empty cells ("blue rectangles");
58         if (cell(idx).empty())
59                 x += 2;
60 }
61
62
63 void MathNestInset::substitute(MathMacro const & m)
64 {
65         for (idx_type i = 0; i < nargs(); ++i)
66                 cell(i).substitute(m);
67 }
68
69
70 void MathNestInset::metrics(MetricsInfo const & mi) const
71 {
72         MetricsInfo m = mi;
73         for (idx_type i = 0; i < nargs(); ++i)
74                 cell(i).metrics(m);
75 }
76
77
78 bool MathNestInset::idxNext(LCursor & cur) const
79 {
80         if (cur.idx() + 1 >= nargs())
81                 return false;
82         ++cur.idx();
83         cur.pos() = 0;
84         return true;
85 }
86
87
88 bool MathNestInset::idxRight(LCursor & cur) const
89 {
90         return idxNext(cur);
91 }
92
93
94 bool MathNestInset::idxPrev(LCursor & cur) const
95 {
96         if (cur.idx() == 0)
97                 return false;
98         --cur.idx();
99         cur.pos() = cur.lastpos();
100         return true;
101 }
102
103
104 bool MathNestInset::idxLeft(LCursor & cur) const
105 {
106         return idxPrev(cur);
107 }
108
109
110 bool MathNestInset::idxFirst(LCursor & cur) const
111 {
112         if (nargs() == 0)
113                 return false;
114         cur.idx() = 0;
115         cur.pos() = 0;
116         return true;
117 }
118
119
120 bool MathNestInset::idxLast(LCursor & cur) const
121 {
122         if (nargs() == 0)
123                 return false;
124         cur.idx() = nargs() - 1;
125         cur.pos() = cur.lastpos();
126         return true;
127 }
128
129
130 bool MathNestInset::idxHome(LCursor & cur) const
131 {
132         if (cur.pos() == 0)
133                 return false;
134         cur.pos() = 0;
135         return true;
136 }
137
138
139 bool MathNestInset::idxEnd(LCursor & cur) const
140 {
141         if (cur.lastpos() == cur.lastpos())
142                 return false;
143         cur.pos() = cur.lastpos();
144         return true;
145 }
146
147
148 void MathNestInset::dump() const
149 {
150         WriteStream os(lyxerr);
151         os << "---------------------------------------------\n";
152         write(os);
153         os << "\n";
154         for (idx_type i = 0; i < nargs(); ++i)
155                 os << cell(i) << "\n";
156         os << "---------------------------------------------\n";
157 }
158
159
160 //void MathNestInset::draw(PainterInfo & pi, int x, int y) const
161 void MathNestInset::draw(PainterInfo &, int, int) const
162 {
163 #if 0
164         if (lock_)
165                 pi.pain.fillRectangle(x, y - ascent(), width(), height(),
166                                         LColor::mathlockbg);
167 #endif
168 }
169
170
171 void MathNestInset::drawSelection(PainterInfo & pi,
172                 idx_type idx1, pos_type pos1, idx_type idx2, pos_type pos2) const
173 {
174         if (idx1 == idx2) {
175                 MathArray const & c = cell(idx1);
176                 int x1 = c.xo() + c.pos2x(pos1);
177                 int y1 = c.yo() - c.ascent();
178                 int x2 = c.xo() + c.pos2x(pos2);
179                 int y2 = c.yo() + c.descent();
180                 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
181         } else {
182                 for (idx_type i = 0; i < nargs(); ++i) {
183                         if (idxBetween(i, idx1, idx2)) {
184                                 MathArray const & c = cell(i);
185                                 int x1 = c.xo();
186                                 int y1 = c.yo() - c.ascent();
187                                 int x2 = c.xo() + c.width();
188                                 int y2 = c.yo() + c.descent();
189                                 pi.pain.fillRectangle(x1, y1, x2 - x1, y2 - y1, LColor::selection);
190                         }
191                 }
192         }
193 }
194
195
196 void MathNestInset::validate(LaTeXFeatures & features) const
197 {
198         for (idx_type i = 0; i < nargs(); ++i)
199                 cell(i).validate(features);
200 }
201
202
203 void MathNestInset::replace(ReplaceData & rep)
204 {
205         for (idx_type i = 0; i < nargs(); ++i)
206                 cell(i).replace(rep);
207 }
208
209
210 bool MathNestInset::contains(MathArray const & ar) const
211 {
212         for (idx_type i = 0; i < nargs(); ++i)
213                 if (cell(i).contains(ar))
214                         return true;
215         return false;
216 }
217
218
219 bool MathNestInset::editing(BufferView * bv) const
220 {
221         return bv->cursor().isInside(this);
222 }
223
224
225 bool MathNestInset::lock() const
226 {
227         return lock_;
228 }
229
230
231 void MathNestInset::lock(bool l)
232 {
233         lock_ = l;
234 }
235
236
237 bool MathNestInset::isActive() const
238 {
239         return nargs() > 0;
240 }
241
242
243 MathArray MathNestInset::glue() const
244 {
245         MathArray ar;
246         for (size_t i = 0; i < nargs(); ++i)
247                 ar.append(cell(i));
248         return ar;
249 }
250
251
252 void MathNestInset::write(WriteStream & os) const
253 {
254         os << '\\' << name().c_str();
255         for (size_t i = 0; i < nargs(); ++i)
256                 os << '{' << cell(i) << '}';
257         if (nargs() == 0)
258                 os.pendingSpace(true);
259         if (lock_ && !os.latex()) {
260                 os << "\\lyxlock";
261                 os.pendingSpace(true);
262         }
263 }
264
265
266 void MathNestInset::normalize(NormalStream & os) const
267 {
268         os << '[' << name().c_str();
269         for (size_t i = 0; i < nargs(); ++i)
270                 os << ' ' << cell(i);
271         os << ']';
272 }
273
274
275 void MathNestInset::notifyCursorLeaves(idx_type idx)
276 {
277         cell(idx).notifyCursorLeaves();
278 }
279
280
281 DispatchResult
282 MathNestInset::priv_dispatch(LCursor & cur, FuncRequest const & cmd)
283 {
284         //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
285         //      << " arg: '" << cmd.argument
286         //      << "' x: '" << cmd.x
287         //      << " y: '" << cmd.y
288         //      << "' button: " << cmd.button() << endl;
289
290         switch (cmd.action) {
291
292         case LFUN_PASTE: {
293                 MathArray ar;
294                 mathed_parse_cell(ar, cmd.argument);
295                 cur.cell().insert(cur.pos(), ar);
296                 cur.pos() += ar.size();
297                 return DispatchResult(true, true);
298         }
299
300         case LFUN_PASTESELECTION:
301                 return dispatch(cur, FuncRequest(LFUN_PASTE, cur.bv().getClipboard())); 
302
303         case LFUN_MOUSE_PRESS:
304                 if (cmd.button() == mouse_button::button2)
305                         return priv_dispatch(cur, FuncRequest(LFUN_PASTESELECTION));
306                 return DispatchResult(false);
307
308         case LFUN_RIGHTSEL:
309                 cur.selection() = true; // fall through...
310         case LFUN_RIGHT:
311                 return cur.right() ?
312                         DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
313                 //lyxerr << "calling scroll 20" << endl;
314                 //scroll(&cur.bv(), 20);
315                 // write something to the minibuffer
316                 //cur.bv().owner()->message(cur.info());
317
318         case LFUN_LEFTSEL:
319                 cur.selection() = true; // fall through
320         case LFUN_LEFT:
321                 return cur.left() ?
322                         DispatchResult(true, true) : DispatchResult(false, FINISHED);
323
324         case LFUN_UPSEL:
325                 cur.selection() = true; // fall through
326         case LFUN_UP:
327                 return cur.up() ?
328                         DispatchResult(true, true) : DispatchResult(false, FINISHED_UP);
329
330         case LFUN_DOWNSEL:
331                 cur.selection() = true; // fall through
332         case LFUN_DOWN:
333                 return cur.down() ?
334                         DispatchResult(true, true) : DispatchResult(false, FINISHED_DOWN);
335
336         case LFUN_WORDSEL:
337                 cur.home();
338                 cur.selection() = true;
339                 cur.end();
340                 return DispatchResult(true, true);
341
342         case LFUN_UP_PARAGRAPHSEL:
343         case LFUN_UP_PARAGRAPH:
344         case LFUN_DOWN_PARAGRAPHSEL:
345         case LFUN_DOWN_PARAGRAPH:
346                 return DispatchResult(true, FINISHED);
347
348         case LFUN_HOMESEL:
349         case LFUN_WORDLEFTSEL:
350                 cur.selection() = true; // fall through
351         case LFUN_HOME:
352         case LFUN_WORDLEFT:
353                 return cur.home()
354                         ? DispatchResult(true, true) : DispatchResult(true, FINISHED);
355
356         case LFUN_ENDSEL:
357         case LFUN_WORDRIGHTSEL:
358                 cur.selection() = true; // fall through
359         case LFUN_END:
360         case LFUN_WORDRIGHT:
361                 return cur.end()
362                         ? DispatchResult(true, true) : DispatchResult(false, FINISHED_RIGHT);
363
364         case LFUN_PRIORSEL:
365         case LFUN_PRIOR:
366         case LFUN_BEGINNINGBUFSEL:
367         case LFUN_BEGINNINGBUF:
368                 return DispatchResult(true, FINISHED);
369
370         case LFUN_NEXTSEL:
371         case LFUN_NEXT:
372         case LFUN_ENDBUFSEL:
373         case LFUN_ENDBUF:
374                 return DispatchResult(false, FINISHED_RIGHT);
375
376         case LFUN_CELL_FORWARD:
377                 cur.inset()->idxNext(cur);
378                 return DispatchResult(true, true);
379
380         case LFUN_CELL_BACKWARD:
381                 cur.inset()->idxPrev(cur);
382                 return DispatchResult(true, true);
383
384         case LFUN_DELETE_WORD_BACKWARD:
385         case LFUN_BACKSPACE:
386                 recordUndo(cur, Undo::ATOMIC);
387                 cur.backspace();
388                 return DispatchResult(true, true);
389
390         case LFUN_DELETE_WORD_FORWARD:
391         case LFUN_DELETE:
392                 recordUndo(cur, Undo::ATOMIC);
393                 cur.erase();
394                 return DispatchResult(true, FINISHED);
395
396         case LFUN_ESCAPE:
397                 if (!cur.selection())
398                         return DispatchResult(true, true);
399                 cur.selClear();
400                 return DispatchResult(false);
401
402         case LFUN_INSET_TOGGLE:
403                 cur.lockToggle();
404                 return DispatchResult(true, true);
405
406         case LFUN_SELFINSERT:
407                 if (!cmd.argument.empty()) {
408                         recordUndo(cur, Undo::ATOMIC);
409                         if (cmd.argument.size() == 1) {
410                                 if (cur.interpret(cmd.argument[0]))
411                                         return DispatchResult(true, true);
412                                 else
413                                         return DispatchResult(false, FINISHED_RIGHT);
414                         }
415                         cur.insert(cmd.argument);
416                 }
417                 return DispatchResult(false, FINISHED_RIGHT);
418
419
420 #if 0
421 //
422 // this needs to bee incorporated
423 //
424         //lyxerr << "InsetFormulaBase::localDispatch: act: " << cmd.action
425         //      << " arg: '" << cmd.argument
426         //      << "' x: '" << cmd.x
427         //      << " y: '" << cmd.y
428         //      << "' button: " << cmd.button() << endl;
429
430         // delete empty mathbox (LFUN_BACKSPACE and LFUN_DELETE)
431         bool remove_inset = false;
432
433         switch (cmd.action) {
434                 case LFUN_MOUSE_PRESS:
435                         //lyxerr << "Mouse single press" << endl;
436                         return lfunMousePress(cur, cmd);
437                 case LFUN_MOUSE_MOTION:
438                         //lyxerr << "Mouse motion" << endl;
439                         return lfunMouseMotion(cur, cmd);
440                 case LFUN_MOUSE_RELEASE:
441                         //lyxerr << "Mouse single release" << endl;
442                         return lfunMouseRelease(cur, cmd);
443                 case LFUN_MOUSE_DOUBLE:
444                         //lyxerr << "Mouse double" << endl;
445                         return dispatch(cur, FuncRequest(LFUN_WORDSEL));
446                 default:
447                         break;
448         }
449
450         DispatchResult result(true);
451         string argument    = cmd.argument;
452         bool was_macro     = cur.inMacroMode();
453
454         cur.normalize();
455         cur.touch();
456
457         switch (cmd.action) {
458
459         case LFUN_MATH_MUTATE:
460         case LFUN_MATH_DISPLAY:
461         case LFUN_MATH_NUMBER:
462         case LFUN_MATH_NONUMBER:
463         case LFUN_CELL_SPLIT:
464         case LFUN_BREAKLINE:
465         case LFUN_DELETE_LINE_FORWARD:
466         case LFUN_INSERT_LABEL:
467         case LFUN_MATH_EXTERN:
468         case LFUN_TABULAR_FEATURE:
469         case LFUN_PASTESELECTION:
470         case LFUN_MATH_LIMITS:
471                 recordUndo(cur, Undo::ATOMIC);
472                 cur.dispatch(cmd);
473                 break;
474
475         //    case LFUN_GETXY:
476         //      sprintf(dispatch_buffer, "%d %d",);
477         //      DispatchResult= dispatch_buffer;
478         //      break;
479         case LFUN_SETXY: {
480                 lyxerr << "LFUN_SETXY broken!" << endl;
481                 int x = 0;
482                 int y = 0;
483                 istringstream is(cmd.argument.c_str());
484                 is >> x >> y;
485                 cur.setScreenPos(x, y);
486                 break;
487         }
488
489         case LFUN_PASTE: {
490                 size_t n = 0;
491                 istringstream is(cmd.argument.c_str());
492                 is >> n;
493                 if (was_macro)
494                         cur.macroModeClose();
495                 recordUndo(cur, Undo::ATOMIC);
496                 cur.selPaste(n);
497                 break;
498         }
499
500         case LFUN_CUT:
501                 recordUndo(cur, Undo::DELETE);
502                 cur.selCut();
503                 break;
504
505         case LFUN_COPY:
506                 cur.selCopy();
507                 break;
508
509
510         // Special casing for superscript in case of LyX handling
511         // dead-keys:
512         case LFUN_CIRCUMFLEX:
513                 if (cmd.argument.empty()) {
514                         // do superscript if LyX handles
515                         // deadkeys
516                         recordUndo(cur, Undo::ATOMIC);
517                         cur.script(true);
518                 }
519                 break;
520
521         case LFUN_UMLAUT:
522         case LFUN_ACUTE:
523         case LFUN_GRAVE:
524         case LFUN_BREVE:
525         case LFUN_DOT:
526         case LFUN_MACRON:
527         case LFUN_CARON:
528         case LFUN_TILDE:
529         case LFUN_CEDILLA:
530         case LFUN_CIRCLE:
531         case LFUN_UNDERDOT:
532         case LFUN_TIE:
533         case LFUN_OGONEK:
534         case LFUN_HUNG_UMLAUT:
535                 break;
536
537         //  Math fonts
538         case LFUN_FREEFONT_APPLY:
539         case LFUN_FREEFONT_UPDATE:
540                 handleFont2(cur, cmd.argument);
541                 break;
542
543         case LFUN_BOLD:         handleFont(cur, cmd.argument, "mathbf"); break;
544         case LFUN_SANS:         handleFont(cur, cmd.argument, "mathsf"); break;
545         case LFUN_EMPH:         handleFont(cur, cmd.argument, "mathcal"); break;
546         case LFUN_ROMAN:        handleFont(cur, cmd.argument, "mathrm"); break;
547         case LFUN_CODE:         handleFont(cur, cmd.argument, "texttt"); break;
548         case LFUN_FRAK:         handleFont(cur, cmd.argument, "mathfrak"); break;
549         case LFUN_ITAL:         handleFont(cur, cmd.argument, "mathit"); break;
550         case LFUN_NOUN:         handleFont(cur, cmd.argument, "mathbb"); break;
551         //case LFUN_FREEFONT_APPLY:  handleFont(cur, cmd.argument, "textrm"); break;
552         case LFUN_DEFAULT:      handleFont(cur, cmd.argument, "textnormal"); break;
553
554         case LFUN_MATH_MODE:
555                 if (cur.currentMode() == InsetBase::TEXT_MODE)
556                         cur.niceInsert(MathAtom(new MathHullInset("simple")));
557                 else
558                         handleFont(cur, cmd.argument, "textrm");
559                 //cur.owner()->message(_("math text mode toggled"));
560                 break;
561
562         case LFUN_MATH_SIZE:
563 #if 0
564                 if (!arg.empty()) {
565                         recordUndo(cur, Undo::ATOMIC);
566                         cur.setSize(arg);
567                 }
568 #endif
569                 break;
570
571         case LFUN_INSERT_MATRIX: {
572                 recordUndo(cur, Undo::ATOMIC);
573                 unsigned int m = 1;
574                 unsigned int n = 1;
575                 string v_align;
576                 string h_align;
577                 istringstream is(argument);
578                 is >> m >> n >> v_align >> h_align;
579                 m = max(1u, m);
580                 n = max(1u, n);
581                 v_align += 'c';
582                 cur.niceInsert(
583                         MathAtom(new MathArrayInset("array", m, n, v_align[0], h_align)));
584                 break;
585         }
586
587         case LFUN_MATH_DELIM: {
588                 //lyxerr << "formulabase::LFUN_MATH_DELIM, arg: '" << arg << "'" << endl;
589                 string ls;
590                 string rs = split(cmd.argument, ls, ' ');
591                 // Reasonable default values
592                 if (ls.empty())
593                         ls = '(';
594                 if (rs.empty())
595                         rs = ')';
596                 recordUndo(cur, Undo::ATOMIC);
597                 cur.handleNest(MathAtom(new MathDelimInset(ls, rs)));
598                 break;
599         }
600
601         case LFUN_SPACE_INSERT:
602         case LFUN_MATH_SPACE:
603                 recordUndo(cur, Undo::ATOMIC);
604                 cur.insert(MathAtom(new MathSpaceInset(",")));
605                 break;
606
607         case LFUN_UNDO:
608                 cur.bv().owner()->message(_("Invalid action in math mode!"));
609                 break;
610
611
612         case LFUN_EXEC_COMMAND:
613                 result = DispatchResult(false);
614                 break;
615
616         case LFUN_INSET_ERT:
617                 // interpret this as if a backslash was typed
618                 recordUndo(cur, Undo::ATOMIC);
619                 cur.interpret('\\');
620                 break;
621
622         case LFUN_BREAKPARAGRAPH:
623         case LFUN_BREAKPARAGRAPHKEEPLAYOUT:
624         case LFUN_BREAKPARAGRAPH_SKIP:
625                 argument = "\n";
626                 // fall through
627
628 // FIXME: We probably should swap parts of "math-insert" and "self-insert"
629 // handling such that "self-insert" works on "arbitrary stuff" too, and
630 // math-insert only handles special math things like "matrix".
631         case LFUN_INSERT_MATH:
632                 recordUndo(cur, Undo::ATOMIC);
633                 cur.niceInsert(argument);
634                 break;
635
636         case LFUN_INSET_TOGGLE:
637                 cur.lockToggle();
638                 break;
639
640         case LFUN_DIALOG_SHOW:
641                 result = DispatchResult(false);
642                 break;
643
644         case LFUN_DIALOG_SHOW_NEW_INSET: {
645                 string const & name = argument;
646                 string data;
647                 if (name == "ref") {
648                         RefInset tmp(name);
649                         data = tmp.createDialogStr(name);
650                 }
651
652                 if (data.empty())
653                         result = DispatchResult(false);
654                 else
655                         cur.bv().owner()->getDialogs().show(name, data, 0);
656                 break;
657         }
658
659         case LFUN_INSET_APPLY: {
660                 string const name = cmd.getArg(0);
661                 InsetBase * base = cur.bv().owner()->getDialogs().getOpenInset(name);
662
663                 if (base) {
664                         FuncRequest fr(LFUN_INSET_MODIFY, cmd.argument);
665                         result = base->dispatch(cur, fr);
666                 } else {
667                         MathArray ar;
668                         if (createMathInset_fromDialogStr(cmd.argument, ar)) {
669                                 cur.insert(ar);
670                                 result = DispatchResult(true, true);
671                         } else {
672                                 result = DispatchResult(false);
673                         }
674                 }
675                 break;
676         }
677
678         case LFUN_WORD_REPLACE:
679         case LFUN_WORD_FIND: {
680                 result = 
681                         searchForward(&cur.bv(), cmd.getArg(0), false, false)
682                                 ? DispatchResult(true, true) : DispatchResult(false);
683                 break;
684         }
685
686         case LFUN_INSERT_MATH:
687         case LFUN_INSERT_MATRIX:
688         case LFUN_MATH_DELIM: {
689                 MathHullInset * f = new MathHullInset;
690                 if (openNewInset(cur, f)) {
691                         cur.inset()->dispatch(cur, FuncRequest(LFUN_MATH_MUTATE, "simple"));
692                         cur.inset()->dispatch(cur, cmd);
693                 }
694                 break;
695         }
696
697         default:
698                 result = DispatchResult(false);
699         }
700
701         if (result == DispatchResult(true, true))
702                 cur.bv().update();
703
704         cur.normalize();
705         cur.touch();
706
707         BOOST_ASSERT(cur.inMathed());
708
709         if (result.dispatched()) {
710                 revealCodes(cur);
711                 cur.bv().stuffClipboard(cur.grabSelection());
712         } else {
713                 cur.releaseMathCursor();
714                 if (remove_inset)
715                         cur.bv().owner()->dispatch(FuncRequest(LFUN_DELETE));
716         }
717
718         return result;  // original version
719 #endif
720
721         default:
722                 return MathInset::priv_dispatch(cur, cmd);
723         }
724 }
725
726
727 void MathNestInset::metricsMarkers(int) const
728 {
729         dim_.wid += 2;
730         dim_.asc += 1;
731 }
732
733
734 void MathNestInset::metricsMarkers2(int) const
735 {
736         dim_.wid += 2;
737         dim_.asc += 1;
738         dim_.des += 1;
739 }
740
741
742 void MathNestInset::drawMarkers(PainterInfo & pi, int x, int y) const
743 {
744         if (!editing(pi.base.bv))
745                 return;
746         int t = x + dim_.width() - 1;
747         int d = y + dim_.descent();
748         pi.pain.line(x, d - 3, x, d, LColor::mathframe);
749         pi.pain.line(t, d - 3, t, d, LColor::mathframe);
750         pi.pain.line(x, d, x + 3, d, LColor::mathframe);
751         pi.pain.line(t - 3, d, t, d, LColor::mathframe);
752 }
753
754
755 void MathNestInset::drawMarkers2(PainterInfo & pi, int x, int y) const
756 {
757         if (!editing(pi.base.bv))
758                 return;
759         drawMarkers(pi, x, y);
760         int t = x + dim_.width() - 1;
761         int a = y - dim_.ascent();
762         pi.pain.line(x, a + 3, x, a, LColor::mathframe);
763         pi.pain.line(t, a + 3, t, a, LColor::mathframe);
764         pi.pain.line(x, a, x + 3, a, LColor::mathframe);
765         pi.pain.line(t - 3, a, t, a, LColor::mathframe);
766 }