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