]> git.lyx.org Git - features.git/blob - src/mathed/math_matrixinset.C
improve support for align and alignat
[features.git] / src / mathed / math_matrixinset.C
1 #ifdef __GNUG__
2 #pragma implementation
3 #endif
4
5 #include <vector>
6
7 #include "math_matrixinset.h"
8 #include "support.h"
9 #include "debug.h"
10 #include "support/LOstream.h"
11 #include "Painter.h"
12 #include "LaTeXFeatures.h"
13
14
15 namespace {
16
17 string getAlign(MathInsetTypes type, int cols)
18 {
19         string align;
20         switch (type) {
21                 case LM_OT_ALIGN:
22                         for (int i = 0; i < cols; ++i)
23                                 align += "Rl";
24                         break;
25
26                 case LM_OT_ALIGNAT:
27                         for (int i = 0; i < cols; ++i)
28                                 align += "rl";
29                         break;
30
31                 case LM_OT_MULTLINE:
32                         align = "C";
33                         break;
34
35                 default:
36                         align = "rcl";
37                         break;
38         }
39         return align;
40 }
41
42
43 string const star(bool n)
44 {
45         return n ? "" : "*";
46 }
47
48
49 int getCols(short int type)
50 {
51         int col;
52         switch (type) {
53                 case LM_OT_EQNARRAY:
54                         col = 3;
55                         break;
56
57                 case LM_OT_ALIGN:
58                 case LM_OT_ALIGNAT:
59                         col = 2;
60                         break;
61
62                 default:
63                         col = 1;
64         }
65         return col;
66 }
67
68 // returns position of first relation operator in the array
69 // used for "intelligent splitting"
70 int firstRelOp(MathArray const & array)
71 {
72         for (MathArray::const_iterator it = array.begin(); it != array.end(); ++it)
73                 if ((*it)->isRelOp())
74                         return it - array.begin();
75         return array.size();
76 }
77
78 }
79
80
81 MathMatrixInset::MathMatrixInset()
82         : MathGridInset(1, 1), objtype_(LM_OT_SIMPLE), nonum_(1), label_(1)
83 {}
84
85
86 MathMatrixInset::MathMatrixInset(MathInsetTypes t)
87         : MathGridInset(getCols(t), 1), objtype_(t), nonum_(1), label_(1)
88 {
89         halign(getAlign(t, ncols()));
90 }
91
92
93 MathMatrixInset::MathMatrixInset(MathInsetTypes t, int cols)
94         : MathGridInset(cols, 1), objtype_(t), nonum_(1), label_(1)
95 {
96         halign(getAlign(t, ncols()));
97 }
98
99
100 MathInset * MathMatrixInset::clone() const
101 {
102         return new MathMatrixInset(*this);
103 }
104
105
106 void MathMatrixInset::metrics(MathStyles) const
107 {
108         size_ = (getType() == LM_OT_SIMPLE) ? LM_ST_TEXT : LM_ST_DISPLAY;
109
110         // let the cells adjust themselves
111         MathGridInset::metrics(size_);
112
113         if (display()) {
114                 ascent_  += 12;
115                 descent_ += 12;
116         }       
117
118         if (numberedType()) {
119                 int l = 0;
120                 for (int row = 0; row < nrows(); ++row)
121                         l = std::max(l, mathed_string_width(LM_TC_BF, size(), nicelabel(row)));
122
123                 if (l)
124                         width_ += 30 + l;
125         }
126
127         // make it at least as high as the current font
128         int asc = 0;
129         int des = 0;
130         math_font_max_dim(LM_TC_TEXTRM, LM_ST_TEXT, asc, des);
131         ascent_  = std::max(ascent_,  asc);
132         descent_ = std::max(descent_, des);
133 }
134
135
136 void MathMatrixInset::draw(Painter & pain, int x, int y) const
137 {
138         xo(x);
139         yo(y);
140
141         MathGridInset::draw(pain, x, y);
142
143         if (numberedType()) {
144                 int xx = x + colinfo_.back().offset_ + colinfo_.back().width_ + 20;
145                 for (int row = 0; row < nrows(); ++row) {
146                         int yy = y + rowinfo_[row].offset_;
147                         drawStr(pain, LM_TC_BF, size(), xx, yy, nicelabel(row));
148                 }
149         }
150 }
151
152
153 void MathMatrixInset::write(std::ostream & os, bool fragile) const
154 {
155   header_write(os);
156
157         bool n = numberedType();
158
159         for (int row = 0; row < nrows(); ++row) {
160                 for (int col = 0; col < ncols(); ++col) {
161                         cell(index(row, col)).write(os, fragile);
162                         os << eocString(col);
163                 }
164                 if (n) {
165                         if (!label_[row].empty())
166                                 os << "\\label{" << label_[row] << "}";
167                         if (nonum_[row])
168                                 os << "\\nonumber ";
169                 }
170                 os << eolString(row);
171         }
172
173   footer_write(os);
174 }
175
176
177 string MathMatrixInset::label(int row) const
178 {
179         return label_[row];
180 }
181
182
183 void MathMatrixInset::label(int row, string const & label)
184 {
185         label_[row] = label; 
186 }
187
188
189 void MathMatrixInset::numbered(int row, bool num)
190 {
191         nonum_[row] = !num; 
192 }
193
194
195 bool MathMatrixInset::numbered(int row) const
196 {
197         return !nonum_[row];
198 }
199
200
201 bool MathMatrixInset::ams() const
202 {
203         return true;
204 }
205
206
207 bool MathMatrixInset::display() const
208 {
209         return getType() != LM_OT_SIMPLE;
210 }
211
212
213 std::vector<string> const MathMatrixInset::getLabelList() const
214 {
215         std::vector<string> res;
216         for (int row = 0; row < nrows(); ++row)
217                 if (!label_[row].empty() && nonum_[row] != 1)
218                         res.push_back(label_[row]);
219         return res;
220 }
221
222
223 bool MathMatrixInset::numberedType() const
224 {
225         if (getType() == LM_OT_SIMPLE)
226                 return false;
227         for (int row = 0; row < nrows(); ++row)
228                 if (!nonum_[row])
229                         return true;
230         return false;
231 }
232
233
234 void MathMatrixInset::validate(LaTeXFeatures & features) const
235 {
236         features.amsstyle = ams();
237
238         // Validation is necessary only if not using AMS math.
239         // To be safe, we will always run mathedvalidate.
240         //if (features.amsstyle)
241         //  return;
242
243         features.boldsymbol = true;
244         //features.binom      = true;
245
246         MathNestInset::validate(features);
247 }
248
249
250 void MathMatrixInset::header_write(std::ostream & os) const
251 {
252         bool n = numberedType();
253
254         switch (getType()) {
255                 case LM_OT_SIMPLE:
256                         os << '$';
257                         if (cell(0).empty())
258                                 os << ' ';
259                         break;
260
261                 case LM_OT_EQUATION:
262                         if (n)
263                                 os << "\\begin{equation" << star(n) << "}\n"; 
264                         else
265                                 os << "\\[\n"; 
266                         break;
267
268                 case LM_OT_EQNARRAY:
269                         os << "\\begin{eqnarray" << star(n) << "}\n";
270                         break;
271
272                 case LM_OT_ALIGN:
273                         os << "\\begin{align" << star(n) << "}";
274                         break;
275
276                 case LM_OT_ALIGNAT:
277                         os << "\\begin{alignat" << star(n) << "}"
278                            << "{" << ncols()/2 << "}\n";
279                         break;
280                 default:
281                         os << "\\begin{unknown" << star(n) << "}";
282         }
283 }
284
285
286 void MathMatrixInset::footer_write(std::ostream & os) const
287 {
288         bool n = numberedType();
289
290         switch (getType()) {
291                 case LM_OT_SIMPLE:
292                         os << '$';
293                         break;
294
295                 case LM_OT_EQUATION:
296                         if (n)
297                                 os << "\\end{equation" << star(n) << "}\n"; 
298                         else
299                                 os << "\\]\n"; 
300                         break;
301
302                 case LM_OT_EQNARRAY:
303                         os << "\\end{eqnarray" << star(n) << "}\n";
304                         break;
305
306                 case LM_OT_ALIGN:
307                         os << "\\end{align" << star(n) << "}\n";
308                         break;
309
310                 case LM_OT_ALIGNAT:
311                         os << "\\end{alignat" << star(n) << "}\n";
312                         break;
313
314                 default:
315                         os << "\\end{unknown" << star(n) << "}";
316         }
317 }
318
319
320 void MathMatrixInset::addRow(int row) 
321 {
322         nonum_.insert(nonum_.begin() + row + 1, !numberedType());
323         label_.insert(label_.begin() + row + 1, string());
324         MathGridInset::addRow(row);
325 }
326
327 void MathMatrixInset::appendRow()
328 {
329         nonum_.push_back(!numberedType());
330         label_.push_back(string());
331         MathGridInset::appendRow();
332 }
333
334
335 void MathMatrixInset::delRow(int row) 
336 {
337         MathGridInset::delRow(row);
338         nonum_.erase(nonum_.begin() + row);
339         label_.erase(label_.begin() + row);
340 }
341
342 void MathMatrixInset::addCol(int col)
343 {
344         switch (getType()) {
345                 case LM_OT_EQUATION:
346                         mutate(LM_OT_EQNARRAY);
347                         break;
348
349                 case LM_OT_EQNARRAY:
350                         mutate(LM_OT_ALIGN);
351                         addCol(col);
352                         break;
353
354                 case LM_OT_ALIGN:
355                 case LM_OT_ALIGNAT:
356                         MathGridInset::addCol(col);
357                         halign(col, 'l');
358                         MathGridInset::addCol(col);
359                         halign(col, 'r');
360                         break;
361
362                 default:
363                         break;
364         }
365 }
366
367 void MathMatrixInset::delCol(int col)
368 {
369         switch (getType()) {
370                 case LM_OT_ALIGN:
371                         MathGridInset::delCol(col);
372                         break;
373
374                 default:
375                         break;
376         }
377 }
378
379
380 string MathMatrixInset::nicelabel(int row) const
381 {
382         if (nonum_[row])
383                 return string();
384         if (label_[row].empty())
385                 return string("(#)");
386         return "(" + label_[row] + ")";
387 }
388
389
390 namespace {
391         short typecode(string const & s)
392         {
393                 if (s == "equation")
394                         return LM_OT_EQUATION;
395                 if (s == "display")
396                         return LM_OT_EQUATION;
397                 if (s == "eqnarray")
398                         return LM_OT_EQNARRAY;
399                 if (s == "align")
400                         return LM_OT_ALIGN;
401                 if (s == "xalign")
402                         return LM_OT_XALIGN;
403                 if (s == "xxalign")
404                         return LM_OT_XXALIGN;
405                 if (s == "multline")
406                         return LM_OT_MULTLINE;
407                 return LM_OT_SIMPLE;
408         }       
409 }
410
411 void MathMatrixInset::mutate(string const & newtype)
412 {
413         if (newtype == "dump") {
414                 dump();
415                 return;
416         }
417         //lyxerr << "mutating from '" << getType() << "' to '" << newtype << "'\n";
418         mutate(typecode(newtype));
419 }
420
421 void MathMatrixInset::glueall()
422 {
423         MathArray ar;
424         for (int i = 0; i < nargs(); ++i)
425                 ar.push_back(cell(i));
426         *this = MathMatrixInset(LM_OT_SIMPLE);
427         cell(0) = ar;
428 }
429
430
431 MathInsetTypes MathMatrixInset::getType() const
432 {
433         return objtype_;
434 }
435
436
437 void MathMatrixInset::setType(MathInsetTypes t)
438 {
439         objtype_ = t;
440 }
441
442
443
444 void MathMatrixInset::mutate(short newtype)
445 {
446         //lyxerr << "mutating from '" << getType() << "' to '" << newtype << "'\n";
447
448         if (newtype == getType())
449                 return;
450
451         switch (getType()) {
452                 case LM_OT_SIMPLE:
453                         setType(LM_OT_EQUATION);
454                         numbered(0, false);
455                         mutate(newtype);
456                         break;
457
458                 case LM_OT_EQUATION:
459                         switch (newtype) {
460                                 case LM_OT_SIMPLE:
461                                         setType(LM_OT_SIMPLE);
462                                         break;
463
464                                 case LM_OT_ALIGN: {
465                                         MathGridInset::addCol(1);
466
467                                         // split it "nicely"
468                                         int pos = firstRelOp(cell(0));  
469                                         cell(1) = cell(0);
470                                         cell(0).erase(pos, cell(0).size());
471                                         cell(1).erase(0, pos);
472
473                                         halign("rl");
474                                         setType(LM_OT_ALIGN);
475                                         break;
476                                 }
477
478                                 case LM_OT_EQNARRAY:
479                                 default:
480                                         MathGridInset::addCol(1);
481                                         MathGridInset::addCol(1);
482
483                                         // split it "nicely" on the firest relop
484                                         int pos = firstRelOp(cell(0));  
485                                         cell(1) = cell(0);
486                                         cell(0).erase(pos, cell(0).size());
487                                         cell(1).erase(0, pos);
488
489                                         if (cell(1).size()) {
490                                                 cell(2) = cell(1);
491                                                 cell(1).erase(1, cell(1).size());
492                                                 cell(2).erase(0);
493                                         }
494
495                                         halign("rcl");
496                                         setType(LM_OT_EQNARRAY);
497                                         mutate(newtype);
498                                         break;
499                                 }
500                         break;
501
502                 case LM_OT_EQNARRAY:
503                         switch (newtype) {
504                                 case LM_OT_SIMPLE:
505                                 case LM_OT_EQUATION: {
506                                         // set correct (no)numbering
507                                         bool allnonum = true;
508                                         for (int r = 0; r < nrows(); ++r) {
509                                                 if (!nonum_[r])
510                                                         allnonum = false;
511                                         }
512
513                                         // set first non-empty label
514                                         string label;
515                                         for (int r = 0; r < nrows(); ++r) {
516                                                 if (!label_[r].empty()) {
517                                                         label = label_[r];
518                                                         break;
519                                                 }
520                                         }
521
522                                         glueall();
523
524                                         nonum_[0] = allnonum;
525                                         label_[0] = label;
526                                         mutate(newtype);
527                                         break;
528                                 }
529
530                                 case LM_OT_ALIGN:
531                                 default: {
532                                         for (int row = 0; row < nrows(); ++row) {
533                                                 int c = 3 * row + 1;
534                                                 cell(c).push_back(cell(c + 1));
535                                         }
536                                         MathGridInset::delCol(2);
537                                         setType(LM_OT_ALIGN);
538                                         halign("rl");
539                                         mutate(newtype);
540                                         break;
541                                 }
542                         }
543                         break;
544
545                 case LM_OT_ALIGN:
546                         switch (newtype) {
547                                 case LM_OT_SIMPLE:
548                                 case LM_OT_EQUATION:
549                                 case LM_OT_EQNARRAY:
550                                         MathGridInset::addCol(1);
551                                         setType(LM_OT_EQNARRAY);
552                                         halign("rcl");
553                                         mutate(newtype);
554                                         break;
555                                 
556                                 default:
557                                         lyxerr << "mutation from '" << getType()
558                                                 << "' to '" << newtype << "' not implemented\n";
559                                         break;
560                         }
561                         break;
562
563                 default:
564                         lyxerr << "mutation from '" << getType()
565                                 << "' to '" << newtype << "' not implemented\n";
566         }
567 }