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