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