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