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