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