]> git.lyx.org Git - lyx.git/blob - src/mathed/math_hullinset.C
fix parser bug for \begin{align}...\end{align}
[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 "debug.h"
12 #include "Painter.h"
13 #include "LaTeXFeatures.h"
14 #include "support/LAssert.h"
15
16 #include <vector>
17
18 using std::endl;
19
20 namespace {
21
22         int getCols(MathInsetTypes type)
23         {
24                 switch (type) {
25                         case LM_OT_EQNARRAY:
26                                 return 3;
27                         case LM_OT_ALIGN:
28                         case LM_OT_ALIGNAT:
29                         case LM_OT_XALIGNAT:
30                         case LM_OT_XXALIGNAT:
31                                 return 2;
32                         default:;
33                 }
34                 return 1;
35         }
36
37
38         // returns position of first relation operator in the array
39         // used for "intelligent splitting"
40         MathArray::size_type firstRelOp(MathArray const & ar)
41         {
42                 for (MathArray::const_iterator it = ar.begin(); it != ar.end(); ++it)
43                         if ((*it)->isRelOp())
44                                 return it - ar.begin();
45                 return ar.size();
46         }
47
48
49         char const * star(bool numbered)
50         {
51                 return numbered ? "" : "*";
52         }
53
54         MathInsetTypes typecode(string const & s)
55         {
56                 if (s == "equation")  return LM_OT_EQUATION;
57                 if (s == "display")   return LM_OT_EQUATION;
58                 if (s == "eqnarray")  return LM_OT_EQNARRAY;
59                 if (s == "align")     return LM_OT_ALIGN;
60                 if (s == "alignat")   return LM_OT_ALIGNAT;
61                 if (s == "xalignat")  return LM_OT_XALIGNAT;
62                 if (s == "xxalignat") return LM_OT_XXALIGNAT;
63                 if (s == "multline")  return LM_OT_MULTLINE;
64                 if (s == "gather")    return LM_OT_GATHER;
65                 return LM_OT_SIMPLE;
66         }       
67
68
69         string normalName(MathInsetTypes t)
70         {
71                 switch (t) {
72                         case LM_OT_EQUATION:  return "equation";
73                         case LM_OT_EQNARRAY:  return "eqnarray";
74                         case LM_OT_ALIGN:     return "align";
75                         case LM_OT_ALIGNAT:   return "alignat";
76                         case LM_OT_XALIGNAT:  return "xalignat";
77                         case LM_OT_XXALIGNAT: return "xxalignat";
78                         case LM_OT_MULTLINE:  return "multline";
79                         case LM_OT_GATHER:    return "gather";
80                         case LM_OT_SIMPLE:    return "simple";
81                         default: break;
82                 }
83                 return "unknown";
84         }       
85
86 } // end anon namespace
87
88
89 MathHullInset::MathHullInset()
90         : MathGridInset(1, 1), objtype_(LM_OT_SIMPLE), nonum_(1), label_(1)
91 {
92         setDefaults();
93 }
94
95
96 MathHullInset::MathHullInset(MathInsetTypes t)
97         : MathGridInset(getCols(t), 1), objtype_(t), nonum_(1), label_(1)
98 {
99         setDefaults();
100 }
101
102
103 MathHullInset::MathHullInset(MathInsetTypes t, MathGridInset const & grid)
104         : MathGridInset(grid), objtype_(t), nonum_(1), label_(1)
105 {
106         setDefaults();
107 }
108
109
110 MathHullInset::MathHullInset(MathInsetTypes t, col_type cols)
111         : MathGridInset(cols, 1), objtype_(t), nonum_(1), label_(1)
112 {
113         setDefaults();
114 }
115
116
117 MathInset * MathHullInset::clone() const
118 {
119         return new MathHullInset(*this);
120 }
121
122
123 char MathHullInset::defaultColAlign(col_type col)
124 {
125         switch (getType()) {
126                 case LM_OT_ALIGN:
127                 case LM_OT_ALIGNAT:
128                 case LM_OT_XALIGNAT:
129                 case LM_OT_XXALIGNAT:
130                         return "rl"[col & 1];
131                 case LM_OT_EQNARRAY:
132                         return "rcl"[col];
133                 default:;
134         }
135         return 'c';
136 }
137
138
139 int MathHullInset::defaultColSpace(col_type col)
140 {
141         switch (getType()) {
142                 case LM_OT_ALIGN:
143                 case LM_OT_ALIGNAT:
144                         return 0;
145                 case LM_OT_XALIGNAT:
146                         return (col & 1) ? 20 : 0;
147                 case LM_OT_XXALIGNAT:
148                         return (col & 1) ? 40 : 0;
149                 default:;
150         }
151         return 0;
152 }
153
154
155 void MathHullInset::metrics(MathMetricsInfo const & mi) const
156 {
157         mi_ = mi;
158         mi_.style = (getType() == LM_OT_SIMPLE) ? LM_ST_TEXT : LM_ST_DISPLAY;
159
160         // let the cells adjust themselves
161         MathGridInset::metrics(mi_);
162
163         if (display()) {
164                 ascent_  += 12;
165                 descent_ += 12;
166         }       
167
168         if (numberedType()) {
169                 int l = 0;
170                 for (row_type row = 0; row < nrows(); ++row)
171                         l = std::max(l, mathed_string_width(LM_TC_BF, mi_, nicelabel(row)));
172
173                 if (l)
174                         width_ += 30 + l;
175         }
176
177         // make it at least as high as the current font
178         int asc = 0;
179         int des = 0;
180         math_font_max_dim(LM_TC_TEXTRM, mi_, asc, des);
181         ascent_  = std::max(ascent_,  asc);
182         descent_ = std::max(descent_, des);
183 }
184
185
186 void MathHullInset::draw(Painter & pain, int x, int y) const
187 {
188         MathGridInset::draw(pain, x, y);
189
190         if (numberedType()) {
191                 int const xx = x + colinfo_.back().offset_ + colinfo_.back().width_ + 20;
192                 for (row_type row = 0; row < nrows(); ++row) {
193                         int const yy = y + rowinfo_[row].offset_;
194                         drawStr(pain, LM_TC_BF, mi_, xx, yy, nicelabel(row));
195                 }
196         }
197 }
198
199
200 string MathHullInset::label(row_type row) const
201 {
202         row_type n = nrows();
203         lyx::Assert(row < n);
204         return label_[row];
205 }
206
207
208 void MathHullInset::label(row_type row, string const & label)
209 {
210         label_[row] = label; 
211 }
212
213
214 void MathHullInset::numbered(row_type row, bool num)
215 {
216         nonum_[row] = !num; 
217 }
218
219
220 bool MathHullInset::numbered(row_type row) const
221 {
222         return !nonum_[row];
223 }
224
225
226 bool MathHullInset::ams() const
227 {
228         return true;
229 /*
230         return 
231                 objtype_ == LM_OT_ALIGN ||
232                 objtype_ == LM_OT_MULTLINE ||
233                 objtype_ == LM_OT_GATHER ||
234                 objtype_ == LM_OT_ALIGNAT ||
235                 objtype_ == LM_OT_XALIGNAT ||
236                 objtype_ == LM_OT_XXALIGNAT;
237 */
238 }
239
240
241 bool MathHullInset::display() const
242 {
243         return getType() != LM_OT_SIMPLE;
244 }
245
246
247 std::vector<string> const MathHullInset::getLabelList() const
248 {
249         std::vector<string> res;
250         for (row_type row = 0; row < nrows(); ++row)
251                 if (!label_[row].empty() && nonum_[row] != 1)
252                         res.push_back(label_[row]);
253         return res;
254 }
255
256
257 bool MathHullInset::numberedType() const
258 {
259         if (getType() == LM_OT_SIMPLE || getType() == LM_OT_XXALIGNAT)
260                 return false;
261         for (row_type row = 0; row < nrows(); ++row)
262                 if (!nonum_[row])
263                         return true;
264         return false;
265 }
266
267
268 void MathHullInset::validate(LaTeXFeatures & features) const
269 {
270         if (ams())
271                 features.require("amsmath");
272
273
274         // Validation is necessary only if not using AMS math.
275         // To be safe, we will always run mathedvalidate.
276         //if (features.amsstyle)
277         //  return;
278
279         features.require("boldsymbol");
280         //features.binom      = true;
281
282         MathNestInset::validate(features);
283 }
284
285
286 void MathHullInset::header_write(WriteStream & os) const
287 {
288         bool n = numberedType();
289
290         switch (getType()) {
291                 case LM_OT_SIMPLE:
292                         os << '$';
293                         if (cell(0).empty())
294                                 os << ' ';
295                         break;
296
297                 case LM_OT_EQUATION:
298                         if (n)
299                                 os << "\\begin{equation" << star(n) << "}\n"; 
300                         else
301                                 os << "\\[\n"; 
302                         break;
303
304                 case LM_OT_EQNARRAY:
305                         os << "\\begin{eqnarray" << star(n) << "}\n";
306                         break;
307
308                 case LM_OT_ALIGN:
309                         os << "\\begin{align" << star(n) << "}\n";
310                         break;
311
312                 case LM_OT_ALIGNAT:
313                         os << "\\begin{alignat" << star(n) << "}"
314                           << "{" << static_cast<unsigned int>(ncols()/2) << "}\n";
315                         break;
316
317                 case LM_OT_XALIGNAT:
318                         os << "\\begin{xalignat" << star(n) << "}"
319                            << "{" << static_cast<unsigned int>(ncols()/2) << "}\n";
320                         break;
321
322                 case LM_OT_XXALIGNAT:
323                         os << "\\begin{xxalignat}" 
324                            << "{" << static_cast<unsigned int>(ncols()/2) << "}\n";
325                         break;
326
327                 case LM_OT_MULTLINE:
328                         os << "\\begin{multline}\n";
329                         break;
330
331                 case LM_OT_GATHER:
332                         os << "\\begin{gather}\n";
333                         break;
334
335                 default:
336                         os << "\\begin{unknown" << star(n) << "}";
337         }
338 }
339
340
341 void MathHullInset::footer_write(WriteStream & os) const
342 {
343         bool n = numberedType();
344
345         switch (getType()) {
346                 case LM_OT_SIMPLE:
347                         os << '$';
348                         break;
349
350                 case LM_OT_EQUATION:
351                         if (n)
352                                 os << "\\end{equation" << star(n) << "}\n"; 
353                         else
354                                 os << "\\]\n"; 
355                         break;
356
357                 case LM_OT_EQNARRAY:
358                         os << "\n\\end{eqnarray" << star(n) << "}\n";
359                         break;
360
361                 case LM_OT_ALIGN:
362                         os << "\n\\end{align" << star(n) << "}\n";
363                         break;
364
365                 case LM_OT_ALIGNAT:
366                         os << "\n\\end{alignat" << star(n) << "}\n";
367                         break;
368
369                 case LM_OT_XALIGNAT:
370                         os << "\n\\end{xalignat" << star(n) << "}\n";
371                         break;
372
373                 case LM_OT_XXALIGNAT:
374                         os << "\n\\end{xxalignat}\n";
375                         break;
376
377                 case LM_OT_MULTLINE:
378                         os << "\n\\end{multline}\n";
379                         break;
380
381                 case LM_OT_GATHER:
382                         os << "\n\\end{gather}\n";
383                         break;
384
385                 default:
386                         os << "\\end{unknown" << star(n) << "}";
387         }
388 }
389
390
391 void MathHullInset::addRow(row_type row) 
392 {
393         nonum_.insert(nonum_.begin() + row + 1, !numberedType());
394         label_.insert(label_.begin() + row + 1, string());
395         MathGridInset::addRow(row);
396 }
397
398
399 void MathHullInset::appendRow()
400 {
401         nonum_.push_back(!numberedType());
402         label_.push_back(string());
403         MathGridInset::appendRow();
404 }
405
406
407 void MathHullInset::delRow(row_type row) 
408 {
409         MathGridInset::delRow(row);
410         nonum_.erase(nonum_.begin() + row);
411         label_.erase(label_.begin() + row);
412 }
413
414
415 void MathHullInset::addCol(col_type col)
416 {
417         switch (getType()) {
418                 case LM_OT_EQUATION:
419                         mutate(LM_OT_EQNARRAY);
420                         break;
421
422                 case LM_OT_EQNARRAY:
423                         mutate(LM_OT_ALIGN);
424                         addCol(col);
425                         break;
426
427                 case LM_OT_ALIGN:
428                         mutate(LM_OT_ALIGNAT);
429                         addCol(col);
430                         break;
431
432                 case LM_OT_ALIGNAT:
433                 case LM_OT_XALIGNAT:
434                 case LM_OT_XXALIGNAT:
435                         MathGridInset::addCol(col);
436                         MathGridInset::addCol(col + 1);
437                         break;
438
439                 default:
440                         break;
441         }
442 }
443
444
445 void MathHullInset::delCol(col_type col)
446 {
447         switch (getType()) {
448                 case LM_OT_ALIGNAT:
449                 case LM_OT_XALIGNAT:
450                 case LM_OT_XXALIGNAT:
451                         MathGridInset::delCol(col + 1);
452                         MathGridInset::delCol(col);
453                         break;
454                 default:
455                         break;
456         }
457 }
458
459
460 string MathHullInset::nicelabel(row_type row) const
461 {
462         if (nonum_[row])
463                 return string();
464         if (label_[row].empty())
465                 return string("(#)");
466         return "(" + label_[row] + ")";
467 }
468
469
470 void MathHullInset::mutate(string const & newtype)
471 {
472         if (newtype == "dump") {
473                 dump();
474                 return;
475         }
476         //lyxerr << "mutating from '" << getType() << "' to '" << newtype << "'\n";
477         mutate(typecode(newtype));
478 }
479
480
481 void MathHullInset::glueall()
482 {
483         MathArray ar;
484         for (idx_type i = 0; i < nargs(); ++i)
485                 ar.push_back(cell(i));
486         *this = MathHullInset(LM_OT_SIMPLE);
487         cell(0) = ar;
488 }
489
490
491 MathInsetTypes MathHullInset::getType() const
492 {
493         return objtype_;
494 }
495
496
497 void MathHullInset::setType(MathInsetTypes t)
498 {
499         objtype_ = t;
500         setDefaults();
501 }
502
503
504
505 void MathHullInset::mutate(MathInsetTypes newtype)
506 {
507         //lyxerr << "mutating from '" << getType() << "' to '" << newtype << "'\n";
508
509         if (newtype == getType())
510                 return;
511
512         switch (getType()) {
513                 case LM_OT_SIMPLE:
514                         setType(LM_OT_EQUATION);
515                         numbered(0, false);
516                         mutate(newtype);
517                         break;
518
519                 case LM_OT_EQUATION:
520                         switch (newtype) {
521                                 case LM_OT_SIMPLE:
522                                         setType(LM_OT_SIMPLE);
523                                         break;
524
525                                 case LM_OT_ALIGN: 
526                                 case LM_OT_ALIGNAT:
527                                 case LM_OT_XALIGNAT:
528                                 case LM_OT_XXALIGNAT: {
529
530                                         MathGridInset::addCol(1);
531
532                                         // split it "nicely"
533                                         pos_type pos = firstRelOp(cell(0));     
534                                         cell(1) = cell(0);
535                                         cell(0).erase(pos, cell(0).size());
536                                         cell(1).erase(0, pos);
537                                         setType(LM_OT_ALIGN);
538                                         mutate(newtype);
539                                         break;
540                                 }
541
542                                 case LM_OT_EQNARRAY:
543                                 default:
544                                         MathGridInset::addCol(1);
545                                         MathGridInset::addCol(1);
546
547                                         // split it "nicely" on the firest relop
548                                         pos_type pos = firstRelOp(cell(0));     
549                                         cell(1) = MathArray(cell(0), pos, cell(0).size());
550                                         cell(0).erase(pos, cell(0).size());
551
552                                         if (cell(1).size()) {
553                                                 cell(2) = MathArray(cell(1), 1, cell(1).size());
554                                                 cell(1).erase(1, cell(1).size());
555                                         }
556
557                                         setType(LM_OT_EQNARRAY);
558                                         mutate(newtype);
559                                         break;
560                                 }
561                         break;
562
563                 case LM_OT_EQNARRAY:
564                         switch (newtype) {
565                                 case LM_OT_SIMPLE:
566                                 case LM_OT_EQUATION: {
567                                         // set correct (no)numbering
568                                         bool allnonum = true;
569                                         for (row_type row = 0; row < nrows(); ++row) {
570                                                 if (!nonum_[row])
571                                                         allnonum = false;
572                                         }
573
574                                         // set first non-empty label
575                                         string label;
576                                         for (row_type row = 0; row < nrows(); ++row) {
577                                                 if (!label_[row].empty()) {
578                                                         label = label_[row];
579                                                         break;
580                                                 }
581                                         }
582
583                                         glueall();
584
585                                         nonum_[0] = allnonum;
586                                         label_[0] = label;
587                                         mutate(newtype);
588                                         break;
589                                 }
590
591                                 case LM_OT_ALIGN:
592                                 case LM_OT_ALIGNAT:
593                                 case LM_OT_XALIGNAT:
594                                 case LM_OT_XXALIGNAT:
595                                 default: {
596                                         for (row_type row = 0; row < nrows(); ++row) {
597                                                 idx_type c = 3 * row + 1;
598                                                 cell(c).push_back(cell(c + 1));
599                                         }
600                                         MathGridInset::delCol(2);
601                                         setType(LM_OT_ALIGN);
602                                         mutate(newtype);
603                                         break;
604                                 }
605                         }
606                         break;
607
608                 case LM_OT_ALIGN:
609                         switch (newtype) {
610                                 case LM_OT_SIMPLE:
611                                 case LM_OT_EQUATION:
612                                 case LM_OT_EQNARRAY:
613                                         MathGridInset::addCol(1);
614                                         setType(LM_OT_EQNARRAY);
615                                         mutate(newtype);
616                                         break;
617                                 
618                                 case LM_OT_ALIGNAT:
619                                 case LM_OT_XALIGNAT:
620                                 case LM_OT_XXALIGNAT:
621                                         setType(newtype);
622                                         break;
623
624                                 default:
625                                         lyxerr << "mutation from '" << getType()
626                                                 << "' to '" << newtype << "' not implemented"
627                                                << endl;
628                                         break;
629                         }
630                         break;
631
632                 case LM_OT_MULTLINE:
633                         switch (newtype) {
634                                 case LM_OT_GATHER:
635                                         setType(LM_OT_GATHER);
636                                         break;
637                                 default:
638                                         lyxerr << "mutation from '" << getType()
639                                                 << "' to '" << newtype << "' not implemented"
640                                                << endl;
641                                         break;
642                         }
643
644                 case LM_OT_GATHER:
645                         switch (newtype) {
646                                 case LM_OT_MULTLINE:
647                                         setType(LM_OT_MULTLINE);
648                                         break;
649                                 default:
650                                         lyxerr << "mutation from '" << getType()
651                                                 << "' to '" << newtype << "' not implemented"
652                                                << endl;
653                                         break;
654                         }
655
656                 default:
657                         lyxerr << "mutation from '" << getType()
658                                << "' to '" << newtype << "' not implemented"
659                                << endl;
660                         break;
661         }
662 }
663
664
665 void MathHullInset::write(WriteStream & os) const
666 {
667         header_write(os);
668         
669         bool n = numberedType();
670         
671         for (row_type row = 0; row < nrows(); ++row) {
672                 for (col_type col = 0; col < ncols(); ++col) 
673                         os << cell(index(row, col)) << eocString(col);
674                 if (n) {
675                         if (!label_[row].empty())
676                                 os << "\\label{" << label_[row] << "}";
677                         if (nonum_[row])
678                                 os << "\\nonumber ";
679                 }
680                 os << eolString(row);
681         }
682         
683         footer_write(os);
684 }
685
686
687 void MathHullInset::normalize(NormalStream & os) const
688 {
689         os << "[formula " << normalName(getType()) << " ";
690         MathGridInset::normalize(os);
691         os << "] ";
692 }
693
694
695 void MathHullInset::mathmlize(MathMLStream & os) const
696 {
697         MathGridInset::mathmlize(os);
698 }
699
700
701 void MathHullInset::check() const
702 {
703         lyx::Assert(nonum_.size() == nrows());
704         lyx::Assert(label_.size() == nrows());
705 }
706
707