]> git.lyx.org Git - lyx.git/blobdiff - src/mathed/math_matrixinset.C
fix pullArg when pressing <Delete> at the end of an cell
[lyx.git] / src / mathed / math_matrixinset.C
index 7fe259dfc3881bf448c6dff52423f60dac5320db..d552e20242c35a77f4e4c3fce6a0f920abb728e7 100644 (file)
-#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(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";
        }
 }