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