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