]> git.lyx.org Git - lyx.git/blob - src/mathed/math_hullinset.C
small up/down tweaking
[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                  || type_ == "gather" || type_ == "multline")
363                         os << "\\begin{" << type_ << star(n) << "}\n";
364
365         else if (type_ == "alignat" || type_ == "xalignat")
366                 os << "\\begin{" << type_ << star(n) << '}'
367                   << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
368
369         else if (type_ == "xxalignat")
370                 os << "\\begin{" << type_ << '}'
371                   << '{' << static_cast<unsigned int>((ncols() + 1)/2) << "}\n";
372
373         else
374                 os << "\\begin{unknown" << star(n) << '}';
375 }
376
377
378 void MathHullInset::footer_write(WriteStream & os) const
379 {
380         bool n = numberedType();
381
382         if (type_ == "none")
383                 os << "\n";
384
385         else if (type_ == "simple")
386                 os << '$';
387
388         else if (type_ == "equation")
389                 if (n)
390                         os << "\\end{equation" << star(n) << "}\n";
391                 else
392                         os << "\\]\n";
393
394         else if (type_ == "eqnarray" || type_ == "align" || type_ == "flalign"
395                  || type_ == "alignat" || type_ == "xalignat"
396                  || type_ == "gather" || type_ == "multline")
397                 os << "\\end{" << type_ << star(n) << "}\n";
398
399         else if (type_ == "xxalignat")
400                 os << "\\end{" << type_ << "}\n";
401
402         else
403                 os << "\\end{unknown" << star(n) << '}';
404 }
405
406
407 bool MathHullInset::colChangeOK() const
408 {
409         return
410                 type_ == "align" || type_ == "flalign" ||type_ == "alignat" ||
411                 type_ == "xalignat" || type_ == "xxalignat";
412 }
413
414
415 void MathHullInset::addRow(row_type row)
416 {
417         nonum_.insert(nonum_.begin() + row + 1, !numberedType());
418         label_.insert(label_.begin() + row + 1, string());
419         MathGridInset::addRow(row);
420 }
421
422
423 void MathHullInset::delRow(row_type row)
424 {
425         if (nrows() <= 1)
426                 return;
427         MathGridInset::delRow(row);
428         nonum_.erase(nonum_.begin() + row);
429         label_.erase(label_.begin() + row);
430 }
431
432
433 void MathHullInset::addCol(col_type col)
434 {
435         if (colChangeOK())
436                 MathGridInset::addCol(col);
437         else
438                 lyxerr << "Can't change number of columns in '" << type_ << "'\n";
439 }
440
441
442 void MathHullInset::delCol(col_type col)
443 {
444         if (colChangeOK())
445                 MathGridInset::delCol(col);
446         else
447                 lyxerr << "Can't change number of columns in '" << type_ << "'\n";
448 }
449
450
451 string MathHullInset::nicelabel(row_type row) const
452 {
453         if (nonum_[row])
454                 return string();
455         if (label_[row].empty())
456                 return string("(#)");
457         return '(' + label_[row] + ')';
458 }
459
460
461 void MathHullInset::glueall()
462 {
463         MathArray ar;
464         for (idx_type i = 0; i < nargs(); ++i)
465                 ar.append(cell(i));
466         *this = MathHullInset("simple");
467         cell(0) = ar;
468         setDefaults();
469 }
470
471
472 string const & MathHullInset::getType() const
473 {
474         return type_;
475 }
476
477
478 void MathHullInset::setType(string const & type)
479 {
480         type_ = type;
481         setDefaults();
482 }
483
484
485
486 void MathHullInset::mutate(string const & newtype)
487 {
488         //lyxerr << "mutating from '" << type_ << "' to '" << newtype << "'\n";
489
490         // we try to move along the chain
491         // none <-> simple <-> equation <-> eqnarray
492
493         if (newtype == "dump") {
494                 dump();
495         }
496
497         else if (newtype == type_) {
498                 // done
499         }
500
501         else if (type_ == "none") {
502                 setType("simple");
503                 numbered(0, false);
504                 mutate(newtype);
505         }
506
507         else if (type_ == "simple") {
508                 if (newtype == "none") {
509                         setType("none");
510                 } else {
511                         setType("equation");
512                         numbered(0, false);
513                         mutate(newtype);
514                 }
515         }
516
517         else if (type_ == "equation") {
518                 if (smaller(newtype, type_)) {
519                         setType("simple");
520                         mutate(newtype);
521                 } else if (newtype == "eqnarray") {
522                         MathGridInset::addCol(1);
523                         MathGridInset::addCol(1);
524
525                         // split it "nicely" on the firest relop
526                         pos_type pos = firstRelOp(cell(0));
527                         cell(1) = MathArray(cell(0).begin() + pos, cell(0).end());
528                         cell(0).erase(pos, cell(0).size());
529
530                         if (cell(1).size()) {
531                                 cell(2) = MathArray(cell(1).begin() + 1, cell(1).end());
532                                 cell(1).erase(1, cell(1).size());
533                         }
534                         setType("eqnarray");
535                         mutate(newtype);
536                 } else if (newtype == "multline" || newtype == "gather") {
537                         setType(newtype);
538                         numbered(0, false);
539                 } else {
540                         MathGridInset::addCol(1);
541                         // split it "nicely"
542                         pos_type pos = firstRelOp(cell(0));
543                         cell(1) = cell(0);
544                         cell(0).erase(pos, cell(0).size());
545                         cell(1).erase(0, pos);
546                         setType("align");
547                         mutate(newtype);
548                 }
549         }
550
551         else if (type_ == "eqnarray") {
552                 if (smaller(newtype, type_)) {
553                         // set correct (no)numbering
554                         bool allnonum = true;
555                         for (row_type row = 0; row < nrows(); ++row)
556                                 if (!nonum_[row])
557                                         allnonum = false;
558
559                         // set first non-empty label
560                         string label;
561                         for (row_type row = 0; row < nrows(); ++row) {
562                                 if (!label_[row].empty()) {
563                                         label = label_[row];
564                                         break;
565                                 }
566                         }
567
568                         glueall();
569                         nonum_[0] = allnonum;
570                         label_[0] = label;
571                         mutate(newtype);
572                 } else { // align & Co.
573                         for (row_type row = 0; row < nrows(); ++row) {
574                                 idx_type c = 3 * row + 1;
575                                 cell(c).append(cell(c + 1));
576                         }
577                         MathGridInset::delCol(2);
578                         setType("align");
579                         mutate(newtype);
580                 }
581         }
582
583         else if (type_ == "align") {
584                 if (smaller(newtype, type_)) {
585                         MathGridInset::addCol(1);
586                         setType("eqnarray");
587                         mutate(newtype);
588                 } else {
589                         setType(newtype);
590                 }
591         }
592
593         else if (type_ == "multline") {
594                 if (newtype == "gather" || newtype == "align" ||
595                     newtype == "xalignat" || newtype == "xxalignat" || newtype == "flalign")
596                         setType(newtype);
597                 else if (newtype == "eqnarray") {
598                         MathGridInset::addCol(1);
599                         MathGridInset::addCol(1);
600                         setType("eqnarray");
601                 } else {
602                         lyxerr << "mutation from '" << type_
603                                 << "' to '" << newtype << "' not implemented" << endl;
604                 }
605         }
606
607         else if (type_ == "gather") {
608                 if (newtype == "multline") {
609                         setType("multline");
610                 } else {
611                         lyxerr << "mutation from '" << type_
612                                 << "' to '" << newtype << "' not implemented" << endl;
613                 }
614         }
615
616         else {
617                 lyxerr << "mutation from '" << type_
618                                          << "' to '" << newtype << "' not implemented" << endl;
619         }
620 }
621
622
623 string MathHullInset::eolString(row_type row, bool fragile) const
624 {
625         string res;
626         if (numberedType()) {
627                 if (!label_[row].empty() && !nonum_[row])
628                         res += "\\label{" + label_[row] + '}';
629                 if (nonum_[row] && (type_ != "multline"))
630                         res += "\\nonumber ";
631         }
632         return res + MathGridInset::eolString(row, fragile);
633 }
634
635
636 void MathHullInset::write(WriteStream & os) const
637 {
638         header_write(os);
639         MathGridInset::write(os);
640         footer_write(os);
641 }
642
643
644 void MathHullInset::normalize(NormalStream & os) const
645 {
646         os << "[formula " << type_ << ' ';
647         MathGridInset::normalize(os);
648         os << "] ";
649 }
650
651
652 void MathHullInset::mathmlize(MathMLStream & os) const
653 {
654         MathGridInset::mathmlize(os);
655 }
656
657
658 void MathHullInset::infoize(std::ostream & os) const
659 {
660         os << "Type: " << type_;
661 }
662
663
664 void MathHullInset::check() const
665 {
666         lyx::Assert(nonum_.size() == nrows());
667         lyx::Assert(label_.size() == nrows());
668 }
669
670
671 void MathHullInset::doExtern
672         (FuncRequest const & func, idx_type & idx, pos_type & pos)
673 {
674         string lang;
675         string extra;
676         istringstream iss(func.argument.c_str());
677         iss >> lang >> extra;
678         if (extra.empty())
679                 extra = "noextra";
680
681 #ifdef WITH_WARNINGS
682 #warning temporarily disabled
683         //if (selection()) {
684         //      MathArray ar;
685         //      selGet(ar);
686         //      lyxerr << "use selection: " << ar << "\n";
687         //      insert(pipeThroughExtern(lang, extra, ar));
688         //      return;
689         //}
690 #endif
691
692         MathArray eq;
693         eq.push_back(MathAtom(new MathCharInset('=')));
694
695         // go to first item in line
696         idx -= idx % ncols();
697         pos = 0;
698
699         if (getType() == "simple") {
700                 size_type pos = cell(idx).find_last(eq);
701                 MathArray ar;
702                 if (mathcursor && mathcursor->selection()) {
703                         ar = asArray(mathcursor->grabAndEraseSelection());
704                 } else if (pos == cell(idx).size()) {
705                         ar = cell(idx);
706                         lyxerr << "use whole cell: " << ar << "\n";
707                 } else {
708                         ar = MathArray(cell(idx).begin() + pos + 1, cell(idx).end());
709                         lyxerr << "use partial cell form pos: " << pos << "\n";
710                 }
711                 cell(idx).append(eq);
712                 cell(idx).append(pipeThroughExtern(lang, extra, ar));
713                 pos = cell(idx).size();
714                 return;
715         }
716
717         if (getType() == "equation") {
718                 lyxerr << "use equation inset\n";
719                 mutate("eqnarray");
720                 MathArray & ar = cell(idx);
721                 lyxerr << "use cell: " << ar << "\n";
722                 cell(idx + 1) = eq;
723                 cell(idx + 2) = pipeThroughExtern(lang, extra, ar);
724                 // move to end of line
725                 idx += 2;
726                 pos = cell(idx).size();
727                 return;
728         }
729
730         {
731                 lyxerr << "use eqnarray\n";
732                 idx -= idx % ncols();
733                 idx += 2;
734                 pos = 0;
735                 MathArray ar = cell(idx);
736                 lyxerr << "use cell: " << ar << "\n";
737 #ifdef WITH_WARNINGS
738 #warning temporarily disabled
739 #endif
740                 addRow(row(idx));
741                 cell(idx + 2) = eq;
742                 cell(idx + 3) = pipeThroughExtern(lang, extra, ar);
743                 idx += 3;
744                 pos = cell(idx).size();
745         }
746 }
747
748
749 MathInset::result_type MathHullInset::dispatch
750         (FuncRequest const & cmd, idx_type & idx, pos_type & pos)
751 {
752         switch (cmd.action) {
753
754                 case LFUN_BREAKLINE:
755                         if (type_ == "simple" || type_ == "equation") {
756                                 mutate("eqnarray");
757                                 idx = 1;
758                                 pos = 0;
759                                 return DISPATCHED_POP;
760                         }
761                         return MathGridInset::dispatch(cmd, idx, pos);
762
763                 case LFUN_MATH_NUMBER:
764                         //lyxerr << "toggling all numbers\n";
765                         if (display()) {
766                                 //bv->lockedInsetStoreUndo(Undo::INSERT);
767                                 bool old = numberedType();
768                                 if (type_ == "multline")
769                                         numbered(nrows() - 1, !old);
770                                 else
771                                         for (row_type row = 0; row < nrows(); ++row)
772                                                 numbered(row, !old);
773                                 //bv->owner()->message(old ? _("No number") : _("Number"));
774                                 //updateLocal(bv, true);
775                         }
776                         return DISPATCHED;
777
778                 case LFUN_MATH_NONUMBER:
779                         if (display()) {
780                                 row_type r = (type_ == "multline") ? nrows() - 1 : row(idx);
781                                 //bv->lockedInsetStoreUndo(Undo::INSERT);
782                                 bool old = numbered(r);
783                                 //bv->owner()->message(old ? _("No number") : _("Number"));
784                                 numbered(r, !old);
785                                 //updateLocal(bv, true);
786                         }
787                         return DISPATCHED;
788
789                 case LFUN_INSERT_LABEL: {
790                         row_type r = (type_ == "multline") ? nrows() - 1 : row(idx);
791                         string old_label = label(r);
792                         string new_label = cmd.argument;
793
794                         if (new_label.empty()) {
795                                 string const default_label =
796                                         (lyxrc.label_init_length >= 0) ? "eq:" : "";
797                                 pair<bool, string> const res = old_label.empty()
798                                         ? Alert::askForText(_("Enter new label to insert:"), default_label)
799                                         : Alert::askForText(_("Enter label:"), old_label);
800                                 if (!res.first)
801                                         break;
802                                 new_label = trim(res.second);
803                         }
804
805                         //if (new_label == old_label)
806                         //      break;  // Nothing to do
807
808                         if (!new_label.empty())
809                                 numbered(r, true);
810
811 #warning FIXME: please check you really mean repaint() ... is it needed,
812 #warning and if so, should it be update() instead ?
813                         if (!new_label.empty()
814                                         && cmd.view()->ChangeRefsIfUnique(old_label, new_label))
815                                 cmd.view()->repaint();
816
817                         label(r, new_label);
818                         return DISPATCHED;
819                 }
820
821                 case LFUN_MATH_EXTERN:
822                         doExtern(cmd, idx, pos);
823                         return DISPATCHED_POP;
824
825                 case LFUN_MATH_MUTATE: {
826                         row_type r = row(idx);
827                         col_type c = col(idx);
828                         mutate(cmd.argument);
829                         idx = r * ncols() + c;
830                         if (idx >= nargs())
831                                 idx = nargs() - 1;
832                         if (pos > cell(idx).size())
833                                 pos = cell(idx).size();
834                         return DISPATCHED_POP;
835                 }
836
837                 case LFUN_MATH_DISPLAY: {
838                         mutate(type_ == "simple" ? "equation" : "simple");
839                         idx = 0;
840                         pos = cell(idx).size();
841                         return DISPATCHED_POP;
842                 }
843
844                 default:
845                         return MathGridInset::dispatch(cmd, idx, pos);
846
847         }
848         return UNDISPATCHED;
849 }