-#include <config.h>
+#ifdef __GNUG__
+#pragma implementation
+#endif
+
+#include <vector>
#include "math_matrixinset.h"
-#include "math_rowst.h"
-#include "math_xiter.h"
+#include "debug.h"
#include "support/LOstream.h"
+#include "Painter.h"
+#include "LaTeXFeatures.h"
-using std::ostream;
-extern int number_of_newlines;
+namespace {
-MathMatrixInset::MathMatrixInset(int m, int n, short st)
- : MathParInset(st, "array", LM_OT_MATRIX), nc_(m), nr_(0), ws_(m),
- v_align_(0), h_align_(nc_, 'c'), row_(0)
+string const getAlign(short int type, int cols)
{
- flag = 15;
- if (n > 0) {
- row_ = new MathedRowSt(nc_ + 1);
- MathedXIter it(this);
- for (int j = 1; j < n; ++j) it.addRow();
- nr_ = n;
- if (nr_ == 1 && nc_ > 1) {
- for (int j = 0; j < nc_ - 1; ++j)
- it.insert('T', LM_TC_TAB);
- }
- } else if (n < 0) {
- row_ = new MathedRowSt(nc_ + 1);
- nr_ = 1;
+ string align;
+ switch (type) {
+ case LM_OT_ALIGN:
+ for (int i = 0; i < cols; ++i)
+ align += "Rl";
+ break;
+
+ case LM_OT_ALIGNAT:
+ for (int i = 0; i < cols; ++i)
+ align += "rl";
+ break;
+
+ case LM_OT_MULTLINE:
+ align = "C";
+ break;
+
+ default:
+ align = "rcl";
+ break;
}
+ return align;
}
-MathMatrixInset::MathMatrixInset(MathMatrixInset * mt)
- : MathParInset(mt->GetStyle(), mt->GetName(), mt->GetType()),
- nc_(mt->nc_), nr_(0), ws_(mt->nc_),
- v_align_(mt->v_align_), h_align_(mt->h_align_)
+string const star(bool n)
{
- MathedIter it;
- it.SetData(mt->GetData());
- array = it.Copy();
- if (mt->row_ != 0) {
- MathedRowSt * ro = 0;
- MathedRowSt * mrow = mt->row_;
-
- while (mrow) {
- MathedRowSt * r = new MathedRowSt(nc_ + 1);
- r->setNumbered(mrow->isNumbered());
- //if (mrow->label)
- r->setLabel(mrow->getLabel());
- if (!ro)
- row_ = r;
- else
- ro->setNext(r);
- mrow = mrow->getNext();
- ro = r;
- ++nr_;
- }
- } else
- row_ = 0;
- flag = mt->flag;
+ return n ? "" : "*";
}
-MathMatrixInset::~MathMatrixInset()
+int getCols(short int type)
{
- MathedRowSt * r = row_;
- while (r) {
- MathedRowSt * q = r->getNext();
- delete r;
- r = q;
+ int col;
+ switch (type) {
+ case LM_OT_EQNARRAY:
+ col = 3;
+ break;
+
+ case LM_OT_ALIGN:
+ case LM_OT_ALIGNAT:
+ col = 2;
+ break;
+
+ default:
+ col = 1;
}
+ return col;
+}
+
+// returns position of first relation operator in the array
+// used for "intelligent splitting"
+int firstRelOp(MathArray const & array)
+{
+ for (int pos = 0; pos < array.size(); array.next(pos))
+ if (!array.isInset(pos) &&
+ MathIsRelOp(array.GetChar(pos), array.GetCode(pos)))
+ return pos;
+ return array.size();
+}
+
+}
+
+MathMatrixInset::MathMatrixInset(MathInsetTypes t)
+ : MathGridInset(getCols(t), 1, "formula", t), nonum_(1), label_(1)
+{}
+
+
+MathMatrixInset::MathMatrixInset()
+ : MathGridInset(1, 1, "formula", LM_OT_SIMPLE), nonum_(1), label_(1)
+{}
+
+MathInset * MathMatrixInset::clone() const
+{
+ return new MathMatrixInset(*this);
}
-MathedInset * MathMatrixInset::Clone()
+void MathMatrixInset::Metrics(MathStyles /* st */, int, int)
{
- return new MathMatrixInset(this);
+ size_ = (GetType() == LM_OT_SIMPLE) ? LM_ST_TEXT : LM_ST_DISPLAY;
+
+ // let the cells adjust themselves
+ MathGridInset::Metrics(size_);
+
+ if (display()) {
+ ascent_ += 12;
+ descent_ += 12;
+ }
+
+ if (numberedType()) {
+ int l = 0;
+ for (int row = 0; row < nrows(); ++row)
+ l = std::max(l, mathed_string_width(LM_TC_BF, size(), nicelabel(row)));
+
+ if (l)
+ width_ += 30 + l;
+ }
}
-void MathMatrixInset::SetAlign(char vv, string const & hh)
+void MathMatrixInset::draw(Painter & pain, int x, int y)
{
- v_align_ = vv;
- h_align_ = hh.substr(0, nc_); // usr just h_align = hh; perhaps
+ xo(x);
+ yo(y);
+
+ MathGridInset::draw(pain, x, y);
+
+ if (numberedType()) {
+ int xx = x + colinfo_.back().offset_ + colinfo_.back().width_ + 20;
+ for (int row = 0; row < nrows(); ++row) {
+ int yy = y + rowinfo_[row].offset_;
+ drawStr(pain, LM_TC_BF, size(), xx, yy, nicelabel(row));
+ }
+ }
}
-// Check the number of tabs and crs
-void MathMatrixInset::setData(MathedArray * a)
+void MathMatrixInset::Write(std::ostream & os, bool fragile) const
{
- if (!a) return;
- MathedIter it(a);
- int nn = nc_ - 1;
- nr_ = 1;
- // count tabs per row
- while (it.OK()) {
- if (it.IsTab()) {
- if (nn < 0) {
- it.Delete();
- continue;
- } else {
- // it.Next();
- --nn;
- }
+ header_write(os);
+
+ bool n = numberedType();
+
+ for (int row = 0; row < nrows(); ++row) {
+ if (row)
+ os << " \\\\\n";
+ for (int col = 0; col < ncols(); ++col) {
+ if (col)
+ os << " & ";
+ cell(index(row, col)).Write(os, fragile);
}
- if (it.IsCR()) {
- while (nn > 0) {
- it.insert(' ', LM_TC_TAB);
- --nn;
- }
- nn = nc_ - 1;
- ++nr_;
+ if (n) {
+ if (!label_[row].empty())
+ os << "\\label{" << label_[row] << "}";
+ if (nonum_[row])
+ os << "\\nonumber ";
}
- it.Next();
}
- it.Reset();
-
- // Automatically inserts tabs around bops
- // DISABLED because it's very easy to insert tabs
- array = a;
+
+ footer_write(os);
}
-void MathMatrixInset::draw(Painter & pain, int x, int baseline)
+string MathMatrixInset::label(int row) const
{
- MathParInset::draw(pain, x, baseline);
+ return label_[row];
}
+void MathMatrixInset::label(int row, string const & label)
+{
+ label_[row] = label;
+}
-void MathMatrixInset::Metrics()
+void MathMatrixInset::numbered(int row, bool num)
{
- if (!row_) {
- // lyxerr << " MIDA ";
- MathedXIter it(this);
- row_ = it.adjustVerticalSt();
- }
-
- // Clean the arrays
- MathedRowSt * cxrow = row_;
- while (cxrow) {
- for (int i = 0; i <= nc_; ++i)
- cxrow->setTab(i, 0);
- cxrow = cxrow->getNext();
- }
-
- // Basic metrics
- MathParInset::Metrics();
-
- if (nc_ <= 1 && !row_->getNext()) {
- row_->ascent(ascent);
- row_->descent(descent);
+ nonum_[row] = !num;
+}
+
+
+bool MathMatrixInset::numbered(int row) const
+{
+ return !nonum_[row];
+}
+
+
+bool MathMatrixInset::ams() const
+{
+ return true;
+}
+
+
+bool MathMatrixInset::display() const
+{
+ return GetType() != LM_OT_SIMPLE;
+}
+
+
+std::vector<string> const MathMatrixInset::getLabelList() const
+{
+ std::vector<string> res;
+ for (int row = 0; row < nrows(); ++row)
+ if (!label_[row].empty() && nonum_[row] != 1)
+ res.push_back(label_[row]);
+ return res;
+}
+
+
+bool MathMatrixInset::numberedType() const
+{
+ if (GetType() == LM_OT_SIMPLE)
+ return false;
+ for (int row = 0; row < nrows(); ++row)
+ if (!nonum_[row])
+ return true;
+ return false;
+}
+
+
+void MathMatrixInset::Validate(LaTeXFeatures & features) const
+{
+ features.amsstyle = ams();
+
+ // Validation is necessary only if not using AMS math.
+ // To be safe, we will always run mathedValidate.
+ //if (features.amsstyle)
+ // return;
+
+ features.boldsymbol = true;
+ //features.binom = true;
+
+ MathInset::Validate(features);
+}
+
+
+void MathMatrixInset::header_write(std::ostream & os) const
+{
+ bool n = numberedType();
+
+ switch (GetType()) {
+ case LM_OT_SIMPLE:
+ os << "\\(";
+ break;
+
+ case LM_OT_EQUATION:
+ if (n)
+ os << "\\begin{equation" << star(n) << "}\n";
+ else
+ os << "\\[\n";
+ break;
+
+ case LM_OT_EQNARRAY:
+ os << "\\begin{eqnarray" << star(n) << "}\n";
+ break;
+
+ case LM_OT_ALIGN:
+ os << "\\begin{align" << star(n) << "}";
+ break;
+
+ case LM_OT_ALIGNAT:
+ os << "\\begin{alignat" << star(n) << "}"
+ << "{" << ncols()/2 << "}\n";
+ break;
+ default:
+ os << "\\begin{unknown" << star(n) << "}";
}
-
- // Vertical positions of each row
- cxrow = row_;
- MathedRowSt * cprow = 0;
- int h = 0;
- while (cxrow) {
- for (int i = 0; i < nc_; ++i) {
- if (cxrow == row_ || ws_[i] < cxrow->getTab(i))
- ws_[i] = cxrow->getTab(i);
- if (cxrow->getNext() == 0 && ws_[i] == 0)
- ws_[i] = df_width;
- }
-
- cxrow->setBaseline((cxrow == row_) ?
- cxrow->ascent() :
- cxrow->ascent() + cprow->descent()
- + MATH_ROWSEP + cprow->getBaseline());
- h += cxrow->ascent() + cxrow->descent() + MATH_ROWSEP;
- cprow = cxrow;
- cxrow = cxrow->getNext();
+}
+
+
+void MathMatrixInset::footer_write(std::ostream & os) const
+{
+ bool n = numberedType();
+
+ switch (GetType()) {
+ case LM_OT_SIMPLE:
+ os << "\\)";
+ break;
+
+ case LM_OT_EQUATION:
+ if (n)
+ os << "\\end{equation" << star(n) << "}\n";
+ else
+ os << "\\]\n";
+ break;
+
+ case LM_OT_EQNARRAY:
+ os << "\\end{eqnarray" << star(n) << "}\n";
+ break;
+
+ case LM_OT_ALIGN:
+ os << "\\end{align" << star(n) << "}\n";
+ break;
+
+ case LM_OT_ALIGNAT:
+ os << "\\end{alignat" << star(n) << "}\n";
+ break;
+
+ default:
+ os << "\\end{unknown" << star(n) << "}";
}
-
- int hl = Descent();
- h -= MATH_ROWSEP;
-
- // Compute vertical align
- switch (v_align_) {
- case 't':
- ascent = row_->getBaseline();
- break;
- case 'b':
- ascent = h - hl;
- break;
- default:
- ascent = (row_->getNext()) ? h / 2 : h - hl;
- break;
+}
+
+
+void MathMatrixInset::addRow(int row)
+{
+ nonum_.insert(nonum_.begin() + row + 1, !numberedType());
+ label_.insert(label_.begin() + row + 1, string());
+ MathGridInset::addRow(row);
+}
+
+void MathMatrixInset::appendRow()
+{
+ nonum_.push_back(!numberedType());
+ label_.push_back(string());
+ MathGridInset::appendRow();
+}
+
+
+void MathMatrixInset::delRow(int row)
+{
+ MathGridInset::delRow(row);
+ nonum_.erase(nonum_.begin() + row);
+ label_.erase(label_.begin() + row);
+}
+
+void MathMatrixInset::addCol(int col)
+{
+ switch (GetType()) {
+ case LM_OT_EQUATION:
+ mutate(LM_OT_EQNARRAY);
+ break;
+
+ case LM_OT_EQNARRAY:
+ mutate(LM_OT_ALIGN);
+ addCol(col);
+ break;
+
+ case LM_OT_ALIGN:
+ case LM_OT_ALIGNAT:
+ MathGridInset::addCol(col);
+ halign(col, 'l');
+ MathGridInset::addCol(col);
+ halign(col, 'r');
+ break;
+
+ default:
+ break;
}
- descent = h - ascent + 2;
-
- // Increase ws_[i] for 'R' columns (except the first one)
- for (int i = 1; i < nc_; ++i)
- if (h_align_[i] == 'R')
- ws_[i] += 10 * df_width;
- // Increase ws_[i] for 'C' column
- if (h_align_[0] == 'C')
- if (ws_[0] < 7 * workWidth / 8)
- ws_[0] = 7 * workWidth / 8;
-
- // Adjust local tabs
- cxrow = row_;
- width = MATH_COLSEP;
- while (cxrow) {
- int rg = MATH_COLSEP;
- int lf = 0;
- for (int i = 0; i < nc_; ++i) {
- bool isvoid = false;
- if (cxrow->getTab(i) <= 0) {
- cxrow->setTab(i, df_width);
- isvoid = true;
- }
- switch (h_align_[i]) {
- case 'l':
- lf = 0;
- break;
- case 'c':
- lf = (ws_[i] - cxrow->getTab(i))/2;
- break;
- case 'r':
- case 'R':
- lf = ws_[i] - cxrow->getTab(i);
- break;
- case 'C':
- if (cxrow == row_)
- lf = 0;
- else if (!cxrow->getNext())
- lf = ws_[i] - cxrow->getTab(i);
- else
- lf = (ws_[i] - cxrow->getTab(i))/2;
- break;
- }
- int ww = (isvoid) ? lf : lf + cxrow->getTab(i);
- cxrow->setTab(i, lf + rg);
- rg = ws_[i] - ww + MATH_COLSEP;
- if (cxrow == row_)
- width += ws_[i] + MATH_COLSEP;
- }
- cxrow->setBaseline(cxrow->getBaseline() - ascent);
- cxrow = cxrow->getNext();
+}
+
+void MathMatrixInset::delCol(int col)
+{
+ switch (GetType()) {
+ case LM_OT_ALIGN:
+ MathGridInset::delCol(col);
+ break;
+
+ default:
+ break;
}
}
-void MathMatrixInset::Write(ostream & os, bool fragile)
+string MathMatrixInset::nicelabel(int row) const
{
- if (GetType() == LM_OT_MATRIX) {
- if (fragile)
- os << "\\protect";
- os << "\\begin{"
- << name
- << '}';
- if (v_align_ == 't' || v_align_ == 'b') {
- os << '['
- << char(v_align_)
- << ']';
- }
- os << '{'
- << h_align_
- << "}\n";
- ++number_of_newlines;
+ if (nonum_[row])
+ return string();
+ if (label_[row].empty())
+ return string("(#)");
+ return "(" + label_[row] + ")";
+}
+
+
+namespace {
+ short typecode(string const & s)
+ {
+ if (s == "equation")
+ return LM_OT_EQUATION;
+ if (s == "display")
+ return LM_OT_EQUATION;
+ if (s == "eqnarray")
+ return LM_OT_EQNARRAY;
+ if (s == "align")
+ return LM_OT_ALIGN;
+ if (s == "xalign")
+ return LM_OT_XALIGN;
+ if (s == "xxalign")
+ return LM_OT_XXALIGN;
+ if (s == "multline")
+ return LM_OT_MULTLINE;
+ return LM_OT_SIMPLE;
+ }
+}
+
+void MathMatrixInset::mutate(string const & newtype)
+{
+ if (newtype == "dump") {
+ dump();
+ return;
}
- MathParInset::Write(os, fragile);
- if (GetType() == LM_OT_MATRIX){
- os << "\n";
- if (fragile)
- os << "\\protect";
- os << "\\end{"
- << name
- << '}';
- ++number_of_newlines;
+ //lyxerr << "mutating from '" << GetType() << "' to '" << newtype << "'\n";
+ mutate(typecode(newtype));
+}
+
+void MathMatrixInset::glueall()
+{
+ MathArray ar;
+ for (int i = 0; i < nargs(); ++i)
+ ar.push_back(cell(i));
+ *this = MathMatrixInset(LM_OT_SIMPLE);
+ cell(0) = ar;
+}
+
+void MathMatrixInset::mutate(short newtype)
+{
+ //lyxerr << "mutating from '" << GetType() << "' to '" << newtype << "'\n";
+
+ if (newtype == GetType())
+ return;
+
+ switch (GetType()) {
+ case LM_OT_SIMPLE:
+ SetType(LM_OT_EQUATION);
+ numbered(0, false);
+ mutate(newtype);
+ break;
+
+ case LM_OT_EQUATION:
+ switch (newtype) {
+ case LM_OT_SIMPLE:
+ SetType(LM_OT_SIMPLE);
+ break;
+
+ case LM_OT_ALIGN: {
+ MathGridInset::addCol(1);
+
+ // split it "nicely"
+ int pos = firstRelOp(cell(0));
+ cell(1) = cell(0);
+ cell(0).erase(pos, cell(0).size());
+ cell(1).erase(0, pos);
+
+ halign("rl");
+ SetType(LM_OT_ALIGN);
+ break;
+ }
+
+ case LM_OT_EQNARRAY:
+ default:
+ MathGridInset::addCol(1);
+ MathGridInset::addCol(1);
+
+ // split it "nicely" on the firest relop
+ int pos1 = firstRelOp(cell(0));
+ cell(1) = cell(0);
+ cell(0).erase(pos1, cell(0).size());
+ cell(1).erase(0, pos1);
+ int pos2 = 0;
+ cell(1).next(pos2);
+ cell(2) = cell(1);
+ cell(1).erase(pos2, cell(1).size());
+ cell(2).erase(0, pos2);
+
+ halign("rcl");
+ SetType(LM_OT_EQNARRAY);
+ mutate(newtype);
+ break;
+ }
+ break;
+
+ case LM_OT_EQNARRAY:
+ switch (newtype) {
+ case LM_OT_SIMPLE:
+ case LM_OT_EQUATION: {
+ // set correct (no)numbering
+ bool allnonum = true;
+ for (int r = 0; r < nrows(); ++r) {
+ if (!nonum_[r])
+ allnonum = false;
+ }
+
+ // set first non-empty label
+ string label;
+ for (int r = 0; r < nrows(); ++r) {
+ if (!label_[r].empty()) {
+ label = label_[r];
+ break;
+ }
+ }
+
+ glueall();
+
+ nonum_[0] = allnonum;
+ label_[0] = label;
+ mutate(newtype);
+ break;
+ }
+
+ case LM_OT_ALIGN:
+ default: {
+ for (int row = 0; row < nrows(); ++row) {
+ int c = 3 * row + 1;
+ cell(c).push_back(cell(c + 1));
+ }
+ MathGridInset::delCol(2);
+ SetType(LM_OT_ALIGN);
+ halign("rl");
+ mutate(newtype);
+ break;
+ }
+ }
+ break;
+
+ case LM_OT_ALIGN:
+ switch (newtype) {
+ case LM_OT_SIMPLE:
+ case LM_OT_EQUATION:
+ case LM_OT_EQNARRAY:
+ MathGridInset::addCol(1);
+ SetType(LM_OT_EQNARRAY);
+ halign("rcl");
+ mutate(newtype);
+ break;
+
+ default:
+ lyxerr << "mutation from '" << GetType()
+ << "' to '" << newtype << "' not implemented\n";
+ break;
+ }
+ break;
+
+ default:
+ lyxerr << "mutation from '" << GetType()
+ << "' to '" << newtype << "' not implemented\n";
}
}