]> git.lyx.org Git - lyx.git/blob - src/mathed/math_hullinset.C
Standardise the header blurb in mathed.
[lyx.git] / src / mathed / math_hullinset.C
1 /**
2  * \file math_hullinset.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_hullinset.h"
14 #include "math_mathmlstream.h"
15 #include "math_streamstr.h"
16 #include "math_cursor.h"
17 #include "math_support.h"
18 #include "math_extern.h"
19 #include "math_charinset.h"
20 #include "debug.h"
21 #include "textpainter.h"
22 #include "funcrequest.h"
23 #include "Lsstream.h"
24 #include "LaTeXFeatures.h"
25 #include "support/LAssert.h"
26 #include "frontends/Painter.h"
27
28 #include "frontends/Alert.h"
29 #include "lyxrc.h"
30 #include "gettext.h"
31 #include "BufferView.h"
32
33 #include <vector>
34
35 using namespace lyx::support;
36
37 using std::vector;
38 using std::max;
39 using std::endl;
40 using std::pair;
41 using std::auto_ptr;
42
43 namespace {
44
45         int getCols(string const & type)
46         {
47                 if (type == "eqnarray")
48                         return 3;
49                 if (type == "align")
50                         return 2;
51                 if (type == "flalign")
52                         return 2;
53                 if (type == "alignat")
54                         return 2;
55                 if (type == "xalignat")
56                         return 2;
57                 if (type == "xxalignat")
58                         return 2;
59                 return 1;
60         }
61
62
63         // returns position of first relation operator in the array
64         // used for "intelligent splitting"
65         MathArray::size_type firstRelOp(MathArray const & ar)
66         {
67                 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
68                         if ((*it)->isRelOp())
69                                 return it - ar.begin();
70                 return ar.size();
71         }
72
73
74         char const * star(bool numbered)
75         {
76                 return numbered ? "" : "*";
77         }
78
79
80         int typecode(string const & s)
81         {
82                 if (s == "none")      return 0;
83                 if (s == "simple")    return 1;
84                 if (s == "equation")  return 2;
85                 if (s == "eqnarray")  return 3;
86                 if (s == "align")     return 4;
87                 if (s == "alignat")   return 5;
88                 if (s == "xalignat")  return 6;
89                 if (s == "xxalignat") return 7;
90                 if (s == "multline")  return 8;
91                 if (s == "gather")    return 9;
92                 if (s == "flalign")   return 10;
93                 lyxerr << "unknown hull type '" << s << "'" << endl;
94                 return 0;
95         }
96
97         bool smaller(string const & s, string const & t)
98         {
99                 return typecode(s) < typecode(t);
100         }
101
102
103 } // end anon namespace
104
105
106 MathHullInset::MathHullInset()
107         : MathGridInset(1, 1), type_("none"), nonum_(1), label_(1)
108 {
109         setDefaults();
110 }
111
112
113 MathHullInset::MathHullInset(string const & type)
114         : MathGridInset(getCols(type), 1), type_(type), nonum_(1), label_(1)
115 {
116         setDefaults();
117 }
118
119
120 auto_ptr<InsetBase> MathHullInset::clone() const
121 {
122         return auto_ptr<InsetBase>(new MathHullInset(*this));
123 }
124
125
126 MathInset::mode_type MathHullInset::currentMode() const
127 {
128         if (type_ == "none")
129                 return UNDECIDED_MODE;
130         // definitely math mode ...
131         return MATH_MODE;
132 }
133
134
135 bool MathHullInset::idxFirst(idx_type & idx, pos_type & pos) const
136 {
137         idx = 0;
138         pos = 0;
139         return true;
140 }
141
142
143 bool MathHullInset::idxLast(idx_type & idx, pos_type & pos) const
144 {
145         idx = nargs() - 1;
146         pos = cell(idx).size();
147         return true;
148 }
149
150
151 char MathHullInset::defaultColAlign(col_type col)
152 {
153         if (type_ == "eqnarray")
154                 return "rcl"[col];
155         if (typecode(type_) >= typecode("align"))
156                 return "rl"[col & 1];
157         return 'c';
158 }
159
160
161 int MathHullInset::defaultColSpace(col_type col)
162 {
163         if (type_ == "align" || type_ == "alignat")
164                 return 0;
165         if (type_ == "xalignat")
166                 return (col & 1) ? 20 : 0;
167         if (type_ == "xxalignat" || type_ == "flalign")
168                 return (col & 1) ? 40 : 0;
169         return 0;
170 }
171
172
173 char const * MathHullInset::standardFont() const
174 {
175         if (type_ == "none")
176                 return "lyxnochange";
177         return "mathnormal";
178 }
179
180
181 void MathHullInset::metrics(MetricsInfo & mi, Dimension & dim) const
182 {
183         FontSetChanger dummy1(mi.base, standardFont());
184         StyleChanger dummy2(mi.base, display() ? LM_ST_DISPLAY : LM_ST_TEXT);
185
186         // let the cells adjust themselves
187         MathGridInset::metrics(mi);
188
189         if (display()) {
190                 dim_.asc += 12;
191                 dim_.des += 12;
192         }
193
194         if (numberedType()) {
195                 FontSetChanger dummy(mi.base, "mathbf");
196                 int l = 0;
197                 for (row_type row = 0; row < nrows(); ++row)
198                         l = max(l, mathed_string_width(mi.base.font, nicelabel(row)));
199
200                 if (l)
201                         dim_.wid += 30 + l;
202         }
203
204         // make it at least as high as the current font
205         int asc = 0;
206         int des = 0;
207         math_font_max_dim(mi.base.font, asc, des);
208         dim_.asc = max(dim_.asc, asc);
209         dim_.des = max(dim_.des, des);
210
211         // for markers
212         metricsMarkers2();
213         dim = dim_;
214 }
215
216
217 void MathHullInset::draw(PainterInfo & pi, int x, int y) const
218 {
219         FontSetChanger dummy1(pi.base, standardFont());
220         StyleChanger dummy2(pi.base, display() ? LM_ST_DISPLAY : LM_ST_TEXT);
221         MathGridInset::draw(pi, x + 1, y);
222
223         if (numberedType()) {
224                 int const xx = x + colinfo_.back().offset_ + colinfo_.back().width_ + 20;
225                 for (row_type row = 0; row < nrows(); ++row) {
226                         int const yy = y + rowinfo_[row].offset_;
227                         FontSetChanger dummy(pi.base, "mathrm");
228                         drawStr(pi, pi.base.font, xx, yy, nicelabel(row));
229                 }
230         }
231
232         drawMarkers2(pi, x, y);
233 }
234
235
236 void MathHullInset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
237 {
238         if (display()) {
239                 MathGridInset::metricsT(mi, dim);
240         } else {
241                 ostringstream os;
242                 WriteStream wi(os, false, true);
243                 write(wi);
244                 dim.wid = os.str().size();
245                 dim.asc = 1;
246                 dim.des = 0;
247         }
248 }
249
250
251 void MathHullInset::drawT(TextPainter & pain, int x, int y) const
252 {
253         if (display()) {
254                 MathGridInset::drawT(pain, x, y);
255         } else {
256                 ostringstream os;
257                 WriteStream wi(os, false, true);
258                 write(wi);
259                 pain.draw(x, y, os.str().c_str());
260         }
261 }
262
263
264 string MathHullInset::label(row_type row) const
265 {
266         row_type n = nrows();
267         Assert(row < n);
268         return label_[row];
269 }
270
271
272 void MathHullInset::label(row_type row, string const & label)
273 {
274         //lyxerr << "setting label '" << label << "' for row " << row << endl;
275         label_[row] = label;
276 }
277
278
279 void MathHullInset::numbered(row_type row, bool num)
280 {
281         nonum_[row] = !num;
282 }
283
284
285 bool MathHullInset::numbered(row_type row) const
286 {
287         return !nonum_[row];
288 }
289
290
291 bool MathHullInset::ams() const
292 {
293         return
294                 type_ == "align" ||
295                 type_ == "flalign" ||
296                 type_ == "multline" ||
297                 type_ == "gather" ||
298                 type_ == "alignat" ||
299                 type_ == "xalignat" ||
300                 type_ == "xxalignat";
301 }
302
303
304 bool MathHullInset::display() const
305 {
306         return type_ != "simple" && type_ != "none";
307 }
308
309
310 void MathHullInset::getLabelList(std::vector<string> & labels) const
311 {
312         for (row_type row = 0; row < nrows(); ++row)
313                 if (!label_[row].empty() && nonum_[row] != 1)
314                         labels.push_back(label_[row]);
315 }
316
317
318 bool MathHullInset::numberedType() const
319 {
320         if (type_ == "none")
321                 return false;
322         if (type_ == "simple")
323                 return false;
324         if (type_ == "xxalignat")
325                 return false;
326         for (row_type row = 0; row < nrows(); ++row)
327                 if (!nonum_[row])
328                         return true;
329         return false;
330 }
331
332
333 void MathHullInset::validate(LaTeXFeatures & features) const
334 {
335         if (ams())
336                 features.require("amsmath");
337
338
339         // Validation is necessary only if not using AMS math.
340         // To be safe, we will always run mathedvalidate.
341         //if (features.amsstyle)
342         //  return;
343
344         features.require("boldsymbol");
345         //features.binom      = true;
346
347         MathGridInset::validate(features);
348 }
349
350
351 void MathHullInset::header_write(WriteStream & os) const
352 {
353         bool n = numberedType();
354
355         if (type_ == "none")
356                 ;
357
358         else if (type_ == "simple") {
359                 os << '$';
360                 if (cell(0).empty())
361                         os << ' ';
362         }
363
364         else if (type_ == "equation") {
365                 if (n)
366                         os << "\\begin{equation" << star(n) << "}\n";
367                 else
368                         os << "\\[\n";
369         }
370
371         else if (type_ == "eqnarray" || type_ == "align" || type_ == "flalign"
372                  || type_ == "gather" || type_ == "multline")
373                         os << "\\begin{" << type_ << star(n) << "}\n";
374
375         else if (type_ == "alignat" || type_ == "xalignat")
376                 os << "\\begin{" << type_ << star(n) << '}'
377                   << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
378
379         else if (type_ == "xxalignat")
380                 os << "\\begin{" << type_ << '}'
381                   << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
382
383         else
384                 os << "\\begin{unknown" << star(n) << '}';
385 }
386
387
388 void MathHullInset::footer_write(WriteStream & os) const
389 {
390         bool n = numberedType();
391
392         if (type_ == "none")
393                 os << "\n";
394
395         else if (type_ == "simple")
396                 os << '$';
397
398         else if (type_ == "equation")
399                 if (n)
400                         os << "\\end{equation" << star(n) << "}\n";
401                 else
402                         os << "\\]\n";
403
404         else if (type_ == "eqnarray" || type_ == "align" || type_ == "flalign"
405                  || type_ == "alignat" || type_ == "xalignat"
406                  || type_ == "gather" || type_ == "multline")
407                 os << "\\end{" << type_ << star(n) << "}\n";
408
409         else if (type_ == "xxalignat")
410                 os << "\\end{" << type_ << "}\n";
411
412         else
413                 os << "\\end{unknown" << star(n) << '}';
414 }
415
416
417 bool MathHullInset::colChangeOK() const
418 {
419         return
420                 type_ == "align" || type_ == "flalign" ||type_ == "alignat" ||
421                 type_ == "xalignat" || type_ == "xxalignat";
422 }
423
424
425 void MathHullInset::addRow(row_type row)
426 {
427         nonum_.insert(nonum_.begin() + row + 1, !numberedType());
428         label_.insert(label_.begin() + row + 1, string());
429         MathGridInset::addRow(row);
430 }
431
432
433 void MathHullInset::delRow(row_type row)
434 {
435         if (nrows() <= 1)
436                 return;
437         MathGridInset::delRow(row);
438         nonum_.erase(nonum_.begin() + row);
439         label_.erase(label_.begin() + row);
440 }
441
442
443 void MathHullInset::addCol(col_type col)
444 {
445         if (colChangeOK())
446                 MathGridInset::addCol(col);
447         else
448                 lyxerr << "Can't change number of columns in '" << type_ << "'" << endl;
449 }
450
451
452 void MathHullInset::delCol(col_type col)
453 {
454         if (colChangeOK())
455                 MathGridInset::delCol(col);
456         else
457                 lyxerr << "Can't change number of columns in '" << type_ << "'" << endl;
458 }
459
460
461 string MathHullInset::nicelabel(row_type row) const
462 {
463         if (nonum_[row])
464                 return string();
465         if (label_[row].empty())
466                 return string("(#)");
467         return '(' + label_[row] + ')';
468 }
469
470
471 void MathHullInset::glueall()
472 {
473         MathArray ar;
474         for (idx_type i = 0; i < nargs(); ++i)
475                 ar.append(cell(i));
476         *this = MathHullInset("simple");
477         cell(0) = ar;
478         setDefaults();
479 }
480
481
482 string const & MathHullInset::getType() const
483 {
484         return type_;
485 }
486
487
488 void MathHullInset::setType(string const & type)
489 {
490         type_ = type;
491         setDefaults();
492 }
493
494
495
496 void MathHullInset::mutate(string const & newtype)
497 {
498         //lyxerr << "mutating from '" << type_ << "' to '" << newtype << "'" << endl;
499
500         // we try to move along the chain
501         // none <-> simple <-> equation <-> eqnarray
502
503         if (newtype == "dump") {
504                 dump();
505         }
506
507         else if (newtype == type_) {
508                 // done
509         }
510
511         else if (type_ == "none") {
512                 setType("simple");
513                 numbered(0, false);
514                 mutate(newtype);
515         }
516
517         else if (type_ == "simple") {
518                 if (newtype == "none") {
519                         setType("none");
520                 } else {
521                         setType("equation");
522                         numbered(0, false);
523                         mutate(newtype);
524                 }
525         }
526
527         else if (type_ == "equation") {
528                 if (smaller(newtype, type_)) {
529                         setType("simple");
530                         mutate(newtype);
531                 } else if (newtype == "eqnarray") {
532                         MathGridInset::addCol(1);
533                         MathGridInset::addCol(1);
534
535                         // split it "nicely" on the firest relop
536                         pos_type pos = firstRelOp(cell(0));
537                         cell(1) = MathArray(cell(0).begin() + pos, cell(0).end());
538                         cell(0).erase(pos, cell(0).size());
539
540                         if (cell(1).size()) {
541                                 cell(2) = MathArray(cell(1).begin() + 1, cell(1).end());
542                                 cell(1).erase(1, cell(1).size());
543                         }
544                         setType("eqnarray");
545                         mutate(newtype);
546                 } else if (newtype == "multline" || newtype == "gather") {
547                         setType(newtype);
548                         numbered(0, false);
549                 } else {
550                         MathGridInset::addCol(1);
551                         // split it "nicely"
552                         pos_type pos = firstRelOp(cell(0));
553                         cell(1) = cell(0);
554                         cell(0).erase(pos, cell(0).size());
555                         cell(1).erase(0, pos);
556                         setType("align");
557                         mutate(newtype);
558                 }
559         }
560
561         else if (type_ == "eqnarray") {
562                 if (smaller(newtype, type_)) {
563                         // set correct (no)numbering
564                         bool allnonum = true;
565                         for (row_type row = 0; row < nrows(); ++row)
566                                 if (!nonum_[row])
567                                         allnonum = false;
568
569                         // set first non-empty label
570                         string label;
571                         for (row_type row = 0; row < nrows(); ++row) {
572                                 if (!label_[row].empty()) {
573                                         label = label_[row];
574                                         break;
575                                 }
576                         }
577
578                         glueall();
579                         nonum_[0] = allnonum;
580                         label_[0] = label;
581                         mutate(newtype);
582                 } else { // align & Co.
583                         for (row_type row = 0; row < nrows(); ++row) {
584                                 idx_type c = 3 * row + 1;
585                                 cell(c).append(cell(c + 1));
586                         }
587                         MathGridInset::delCol(2);
588                         setType("align");
589                         mutate(newtype);
590                 }
591         }
592
593         else if (type_ == "align") {
594                 if (smaller(newtype, type_)) {
595                         MathGridInset::addCol(1);
596                         setType("eqnarray");
597                         mutate(newtype);
598                 } else {
599                         setType(newtype);
600                 }
601         }
602
603         else if (type_ == "multline") {
604                 if (newtype == "gather" || newtype == "align" ||
605                     newtype == "xalignat" || newtype == "xxalignat" || newtype == "flalign")
606                         setType(newtype);
607                 else if (newtype == "eqnarray") {
608                         MathGridInset::addCol(1);
609                         MathGridInset::addCol(1);
610                         setType("eqnarray");
611                 } else {
612                         lyxerr << "mutation from '" << type_
613                                 << "' to '" << newtype << "' not implemented" << endl;
614                 }
615         }
616
617         else if (type_ == "gather") {
618                 if (newtype == "multline") {
619                         setType("multline");
620                 } else {
621                         lyxerr << "mutation from '" << type_
622                                 << "' to '" << newtype << "' not implemented" << endl;
623                 }
624         }
625
626         else {
627                 lyxerr << "mutation from '" << type_
628                                          << "' to '" << newtype << "' not implemented" << endl;
629         }
630 }
631
632
633 string MathHullInset::eolString(row_type row, bool fragile) const
634 {
635         string res;
636         if (numberedType()) {
637                 if (!label_[row].empty() && !nonum_[row])
638                         res += "\\label{" + label_[row] + '}';
639                 if (nonum_[row] && (type_ != "multline"))
640                         res += "\\nonumber ";
641         }
642         return res + MathGridInset::eolString(row, fragile);
643 }
644
645
646 void MathHullInset::write(WriteStream & os) const
647 {
648         header_write(os);
649         MathGridInset::write(os);
650         footer_write(os);
651 }
652
653
654 void MathHullInset::normalize(NormalStream & os) const
655 {
656         os << "[formula " << type_ << ' ';
657         MathGridInset::normalize(os);
658         os << "] ";
659 }
660
661
662 void MathHullInset::mathmlize(MathMLStream & os) const
663 {
664         MathGridInset::mathmlize(os);
665 }
666
667
668 void MathHullInset::infoize(std::ostream & os) const
669 {
670         os << "Type: " << type_;
671 }
672
673
674 void MathHullInset::check() const
675 {
676         Assert(nonum_.size() == nrows());
677         Assert(label_.size() == nrows());
678 }
679
680
681 void MathHullInset::doExtern
682         (FuncRequest const & func, idx_type & idx, pos_type & pos)
683 {
684         string lang;
685         string extra;
686         istringstream iss(func.argument.c_str());
687         iss >> lang >> extra;
688         if (extra.empty())
689                 extra = "noextra";
690
691 #ifdef WITH_WARNINGS
692 #warning temporarily disabled
693         //if (selection()) {
694         //      MathArray ar;
695         //      selGet(ar);
696         //      lyxerr << "use selection: " << ar << endl;
697         //      insert(pipeThroughExtern(lang, extra, ar));
698         //      return;
699         //}
700 #endif
701
702         MathArray eq;
703         eq.push_back(MathAtom(new MathCharInset('=')));
704
705         // go to first item in line
706         idx -= idx % ncols();
707         pos = 0;
708
709         if (getType() == "simple") {
710                 size_type pos = cell(idx).find_last(eq);
711                 MathArray ar;
712                 if (mathcursor && mathcursor->selection()) {
713                         asArray(mathcursor->grabAndEraseSelection(), ar);
714                 } else if (pos == cell(idx).size()) {
715                         ar = cell(idx);
716                         lyxerr << "use whole cell: " << ar << endl;
717                 } else {
718                         ar = MathArray(cell(idx).begin() + pos + 1, cell(idx).end());
719                         lyxerr << "use partial cell form pos: " << pos << endl;
720                 }
721                 cell(idx).append(eq);
722                 cell(idx).append(pipeThroughExtern(lang, extra, ar));
723                 pos = cell(idx).size();
724                 return;
725         }
726
727         if (getType() == "equation") {
728                 lyxerr << "use equation inset" << endl;
729                 mutate("eqnarray");
730                 MathArray & ar = cell(idx);
731                 lyxerr << "use cell: " << ar << endl;
732                 cell(idx + 1) = eq;
733                 cell(idx + 2) = pipeThroughExtern(lang, extra, ar);
734                 // move to end of line
735                 idx += 2;
736                 pos = cell(idx).size();
737                 return;
738         }
739
740         {
741                 lyxerr << "use eqnarray" << endl;
742                 idx -= idx % ncols();
743                 idx += 2;
744                 pos = 0;
745                 MathArray ar = cell(idx);
746                 lyxerr << "use cell: " << ar << endl;
747 #ifdef WITH_WARNINGS
748 #warning temporarily disabled
749 #endif
750                 addRow(row(idx));
751                 cell(idx + 2) = eq;
752                 cell(idx + 3) = pipeThroughExtern(lang, extra, ar);
753                 idx += 3;
754                 pos = cell(idx).size();
755         }
756 }
757
758
759 dispatch_result MathHullInset::dispatch
760         (FuncRequest const & cmd, idx_type & idx, pos_type & pos)
761 {
762         switch (cmd.action) {
763
764                 case LFUN_BREAKLINE:
765                         if (type_ == "simple" || type_ == "equation") {
766                                 mutate("eqnarray");
767                                 idx = 1;
768                                 pos = 0;
769                                 return DISPATCHED_POP;
770                         }
771                         return MathGridInset::dispatch(cmd, idx, pos);
772
773                 case LFUN_MATH_NUMBER:
774                         //lyxerr << "toggling all numbers" << endl;
775                         if (display()) {
776                                 //recordUndo(bv, Undo::INSERT);
777                                 bool old = numberedType();
778                                 if (type_ == "multline")
779                                         numbered(nrows() - 1, !old);
780                                 else
781                                         for (row_type row = 0; row < nrows(); ++row)
782                                                 numbered(row, !old);
783                                 //bv->owner()->message(old ? _("No number") : _("Number"));
784                         }
785                         return DISPATCHED;
786
787                 case LFUN_MATH_NONUMBER:
788                         if (display()) {
789                                 row_type r = (type_ == "multline") ? nrows() - 1 : row(idx);
790                                 //recordUndo(bv, Undo::INSERT);
791                                 bool old = numbered(r);
792                                 //bv->owner()->message(old ? _("No number") : _("Number"));
793                                 numbered(r, !old);
794                         }
795                         return DISPATCHED;
796
797                 case LFUN_INSERT_LABEL: {
798                         row_type r = (type_ == "multline") ? nrows() - 1 : row(idx);
799                         string old_label = label(r);
800                         string new_label = cmd.argument;
801
802                         if (new_label.empty()) {
803                                 string const default_label =
804                                         (lyxrc.label_init_length >= 0) ? "eq:" : "";
805                                 pair<bool, string> const res = old_label.empty()
806                                         ? Alert::askForText(_("Enter new label to insert:"), default_label)
807                                         : Alert::askForText(_("Enter label:"), old_label);
808                                 if (!res.first)
809                                         return UNDISPATCHED;
810                                 new_label = trim(res.second);
811                         }
812
813                         //if (new_label == old_label)
814                         //      break;  // Nothing to do
815
816                         if (!new_label.empty())
817                                 numbered(r, true);
818                         label(r, new_label);
819                         return DISPATCHED;
820                 }
821
822                 case LFUN_MATH_EXTERN:
823                         doExtern(cmd, idx, pos);
824                         return DISPATCHED_POP;
825
826                 case LFUN_MATH_MUTATE: {
827                         row_type r = row(idx);
828                         col_type c = col(idx);
829                         mutate(cmd.argument);
830                         idx = r * ncols() + c;
831                         if (idx >= nargs())
832                                 idx = nargs() - 1;
833                         if (pos > cell(idx).size())
834                                 pos = cell(idx).size();
835                         return DISPATCHED_POP;
836                 }
837
838                 case LFUN_MATH_DISPLAY: {
839                         mutate(type_ == "simple" ? "equation" : "simple");
840                         idx = 0;
841                         pos = cell(idx).size();
842                         return DISPATCHED_POP;
843                 }
844
845                 default:
846                         return MathGridInset::dispatch(cmd, idx, pos);
847         }
848 }
849
850
851 string MathHullInset::fileInsetLabel() const
852 {
853         return "Formula";
854 }