]> git.lyx.org Git - lyx.git/blobdiff - src/tabular.C
minimal effort implementation of:
[lyx.git] / src / tabular.C
index 02ddbd7e4645ff6487d2c68c62931177f40f1dde..60c20f9d55c6b5973153283f58d26c10a9912bd6 100644 (file)
 
 #include "buffer.h"
 #include "bufferparams.h"
+#include "BufferView.h"
+#include "cursor.h"
 #include "debug.h"
 #include "LaTeXFeatures.h"
 #include "lyxlex.h"
 #include "outputparams.h"
 #include "paragraph.h"
+#include "paragraph_funcs.h"
 
 #include "insets/insettabular.h"
 
 #include "support/lstrings.h"
-#include "support/tostr.h"
+#include "support/convert.h"
 
 #include <sstream>
 
 using lyx::support::ltrim;
 using lyx::support::prefixIs;
 using lyx::support::rtrim;
-using lyx::support::strToInt;
 using lyx::support::suffixIs;
 
 using boost::shared_ptr;
@@ -62,6 +64,8 @@ using std::strlen;
 namespace {
 
 int const WIDTH_OF_LINE = 5;
+int const default_line_space = 10;
+
 
 template <class T>
 string const write_attribute(string const & name, T const & t)
@@ -249,7 +253,7 @@ bool getTokenValue(string const & str, char const * token, int & num)
        num = 0;
        if (!getTokenValue(str, token, tmp))
                return false;
-       num = strToInt(tmp);
+       num = convert<int>(tmp);
        return true;
 }
 
@@ -297,6 +301,21 @@ bool getTokenValue(string const & str, char const * token, LyXLength & len)
 }
 
 
+bool getTokenValue(string const & str, char const * token, LyXLength & len, bool & flag)
+{
+       len = LyXLength();
+       flag = false;
+       string tmp;
+       if (!getTokenValue(str, token, tmp))
+               return false;
+       if (tmp == "default") {
+               flag = true;
+               return  true;
+       }
+       return isValidLength(tmp, &len);
+}
+
+
 void l_getline(istream & is, string & str)
 {
        str.erase();
@@ -378,6 +397,9 @@ LyXTabular::rowstruct::rowstruct()
          descent_of_row(0),
          top_line(true),
          bottom_line(false),
+         top_space_default(false),
+         bottom_space_default(false),
+         interline_space_default(false),
          endhead(false),
          endfirsthead(false),
          endfoot(false),
@@ -404,7 +426,7 @@ LyXTabular::ltType::ltType()
 
 
 LyXTabular::LyXTabular(BufferParams const & bp, row_type rows_arg,
-                       col_type columns_arg)
+                      col_type columns_arg)
 {
        init(bp, rows_arg, columns_arg);
 }
@@ -412,7 +434,7 @@ LyXTabular::LyXTabular(BufferParams const & bp, row_type rows_arg,
 
 // activates all lines and sets all widths to 0
 void LyXTabular::init(BufferParams const & bp, row_type rows_arg,
-                      col_type columns_arg)
+                     col_type columns_arg)
 {
        rows_    = rows_arg;
        columns_ = columns_arg;
@@ -430,6 +452,7 @@ void LyXTabular::init(BufferParams const & bp, row_type rows_arg,
        column_info.back().right_line = true;
        is_long_tabular = false;
        rotate = false;
+       use_booktabs = false;
 }
 
 
@@ -516,7 +539,7 @@ void LyXTabular::appendColumn(BufferParams const & bp, idx_type const cell)
        }
        //++column;
        for (row_type i = 0; i < rows_; ++i) {
-               cell_info[i][column + 1].inset->clear(false);
+               cell_info[i][column + 1].inset->clear();
                if (bp.tracking_changes)
                        cell_info[i][column + 1].inset->markNew(true);
        }
@@ -605,10 +628,10 @@ LyXTabular::idx_type LyXTabular::numberOfCellsInRow(idx_type const cell) const
 }
 
 
-// returns 1 if there is a topline, returns 0 if not
 bool LyXTabular::topLine(idx_type const cell, bool const onlycolumn) const
 {
-       if (!onlycolumn && isMultiColumn(cell))
+       if (!onlycolumn && isMultiColumn(cell) &&
+           !(use_booktabs && row_of_cell(cell) == 0))
                return cellinfo_of_cell(cell).top_line;
        return row_info[row_of_cell(cell)].top_line;
 }
@@ -616,7 +639,8 @@ bool LyXTabular::topLine(idx_type const cell, bool const onlycolumn) const
 
 bool LyXTabular::bottomLine(idx_type const cell, bool onlycolumn) const
 {
-       if (!onlycolumn && isMultiColumn(cell))
+       if (!onlycolumn && isMultiColumn(cell) &&
+           !(use_booktabs && isLastRow(cell)))
                return cellinfo_of_cell(cell).bottom_line;
        return row_info[row_of_cell(cell)].bottom_line;
 }
@@ -624,6 +648,8 @@ bool LyXTabular::bottomLine(idx_type const cell, bool onlycolumn) const
 
 bool LyXTabular::leftLine(idx_type cell, bool onlycolumn) const
 {
+       if (use_booktabs)
+               return false;
        if (!onlycolumn && isMultiColumn(cell) &&
                (isFirstCellInRow(cell) || isMultiColumn(cell-1)))
        {
@@ -639,6 +665,8 @@ bool LyXTabular::leftLine(idx_type cell, bool onlycolumn) const
 
 bool LyXTabular::rightLine(idx_type cell, bool onlycolumn) const
 {
+       if (use_booktabs)
+               return false;
        if (!onlycolumn && isMultiColumn(cell) &&
                (isLastCellInRow(cell) || isMultiColumn(cell + 1)))
        {
@@ -719,9 +747,12 @@ int LyXTabular::getAdditionalHeight(row_type row) const
                        top = row_info[row].top_line;
                }
        }
+       int const interline_space = row_info[row - 1].interline_space_default ?
+               default_line_space :
+               row_info[row - 1].interline_space.inPixels(width_of_tabular);
        if (top && bottom)
-               return WIDTH_OF_LINE;
-       return 0;
+               return interline_space + WIDTH_OF_LINE;
+       return interline_space;
 }
 
 
@@ -799,9 +830,9 @@ void LyXTabular::recalculateMulticolumnsOfColumn(col_type column)
        // the last column does not have to be recalculated because all
        // multicolumns will have here there last multicolumn cell which
        // always will have the whole rest of the width of the cell.
-       if (column > (columns_ - 2))
+       if (columns_ < 2 || column > (columns_ - 2))
                return;
-       for(row_type row = 0; row < rows_; ++row) {
+       for (row_type row = 0; row < rows_; ++row) {
                int mc = cell_info[row][column].multicolumn;
                int nmc = cell_info[row][column+1].multicolumn;
                // we only have to update multicolumns which do not have this
@@ -818,7 +849,6 @@ void LyXTabular::recalculateMulticolumnsOfColumn(col_type column)
 }
 
 
-// returns 1 if a complete update is necessary, otherwise 0
 void LyXTabular::setWidthOfCell(idx_type cell, int new_width)
 {
        row_type const row = row_of_cell(cell);
@@ -855,7 +885,7 @@ void LyXTabular::setWidthOfCell(idx_type cell, int new_width)
 
 
 void LyXTabular::setAlignment(idx_type cell, LyXAlignment align,
-                              bool onlycolumn)
+                             bool onlycolumn)
 {
        if (!isMultiColumn(cell) || onlycolumn)
                column_info[column_of_cell(cell)].alignment = align;
@@ -865,7 +895,7 @@ void LyXTabular::setAlignment(idx_type cell, LyXAlignment align,
 
 
 void LyXTabular::setVAlignment(idx_type cell, VAlignment align,
-                               bool onlycolumn)
+                              bool onlycolumn)
 {
        if (!isMultiColumn(cell) || onlycolumn)
                column_info[column_of_cell(cell)].valignment = align;
@@ -874,7 +904,38 @@ void LyXTabular::setVAlignment(idx_type cell, VAlignment align,
 }
 
 
-void LyXTabular::setColumnPWidth(idx_type cell, LyXLength const & width)
+namespace {
+
+/**
+ * Allow line and paragraph breaks for fixed width cells or disallow them,
+ * merge cell paragraphs and reset layout to standard for variable width
+ * cells.
+ */
+void toggleFixedWidth(LCursor & cur, InsetText * inset, bool fixedWidth)
+{
+       inset->setAutoBreakRows(fixedWidth);
+       if (fixedWidth)
+               return;
+
+       // merge all paragraphs to one
+       BufferParams const & bp =
+               inset->getText(0)->bv_owner->buffer()->params();
+       while (inset->paragraphs().size() > 1)
+               mergeParagraph(bp, inset->paragraphs(), 0);
+
+       // reset layout
+       cur.push(*inset);
+       // undo information has already been recorded
+       inset->getText(0)->setLayout(0, cur.lastpit() + 1,
+                       bp.getLyXTextClass().defaultLayoutName());
+       cur.pop();
+}
+
+}
+
+
+void LyXTabular::setColumnPWidth(LCursor & cur, idx_type cell,
+               LyXLength const & width)
 {
        col_type const j = column_of_cell(cell);
 
@@ -882,18 +943,32 @@ void LyXTabular::setColumnPWidth(idx_type cell, LyXLength const & width)
        for (row_type i = 0; i < rows_; ++i) {
                idx_type const cell = getCellNumber(i, j);
                // because of multicolumns
-               getCellInset(cell)->setAutoBreakRows(!getPWidth(cell).zero());
+               toggleFixedWidth(cur, getCellInset(cell).get(),
+                                !getPWidth(cell).zero());
        }
+       // cur paragraph can become invalid after paragraphs were merged
+       if (cur.pit() > cur.lastpit())
+               cur.pit() = cur.lastpit();
+       // cur position can become invalid after newlines were removed
+       if (cur.pos() > cur.lastpos())
+               cur.pos() = cur.lastpos();
 }
 
 
-bool LyXTabular::setMColumnPWidth(idx_type cell, LyXLength const & width)
+bool LyXTabular::setMColumnPWidth(LCursor & cur, idx_type cell,
+               LyXLength const & width)
 {
        if (!isMultiColumn(cell))
                return false;
 
        cellinfo_of_cell(cell).p_width = width;
-       getCellInset(cell)->setAutoBreakRows(!width.zero());
+       toggleFixedWidth(cur, getCellInset(cell).get(), !width.zero());
+       // cur paragraph can become invalid after paragraphs were merged
+       if (cur.pit() > cur.lastpit())
+               cur.pit() = cur.lastpit();
+       // cur position can become invalid after newlines were removed
+       if (cur.pos() > cur.lastpos())
+               cur.pos() = cur.lastpos();
        return true;
 }
 
@@ -1147,6 +1222,7 @@ void LyXTabular::write(Buffer const & buf, ostream & os) const
        // global longtable options
        os << "<features"
           << write_attribute("rotate", rotate)
+          << write_attribute("booktabs", use_booktabs)
           << write_attribute("islongtable", is_long_tabular)
           << write_attribute("firstHeadTopDL", endfirsthead.topDL)
           << write_attribute("firstHeadBottomDL", endfirsthead.bottomDL)
@@ -1172,8 +1248,21 @@ void LyXTabular::write(Buffer const & buf, ostream & os) const
        for (row_type i = 0; i < rows_; ++i) {
                os << "<row"
                   << write_attribute("topline", row_info[i].top_line)
-                  << write_attribute("bottomline", row_info[i].bottom_line)
-                  << write_attribute("endhead", row_info[i].endhead)
+                  << write_attribute("bottomline", row_info[i].bottom_line);
+               static const string def("default");
+               if (row_info[i].top_space_default)
+                       os << write_attribute("topspace", def);
+               else
+                       os << write_attribute("topspace", row_info[i].top_space);
+               if (row_info[i].bottom_space_default)
+                       os << write_attribute("bottomspace", def);
+               else
+                       os << write_attribute("bottomspace", row_info[i].bottom_space);
+               if (row_info[i].interline_space_default)
+                       os << write_attribute("interlinespace", def);
+               else
+                       os << write_attribute("interlinespace", row_info[i].interline_space);
+               os << write_attribute("endhead", row_info[i].endhead)
                   << write_attribute("endfirsthead", row_info[i].endfirsthead)
                   << write_attribute("endfoot", row_info[i].endfoot)
                   << write_attribute("endlastfoot", row_info[i].endlastfoot)
@@ -1235,6 +1324,7 @@ void LyXTabular::read(Buffer const & buf, LyXLex & lex)
                return;
        }
        getTokenValue(line, "rotate", rotate);
+       getTokenValue(line, "booktabs", use_booktabs);
        getTokenValue(line, "islongtable", is_long_tabular);
        getTokenValue(line, "firstHeadTopDL", endfirsthead.topDL);
        getTokenValue(line, "firstHeadBottomDL", endfirsthead.bottomDL);
@@ -1271,6 +1361,12 @@ void LyXTabular::read(Buffer const & buf, LyXLex & lex)
                }
                getTokenValue(line, "topline", row_info[i].top_line);
                getTokenValue(line, "bottomline", row_info[i].bottom_line);
+               getTokenValue(line, "topspace", row_info[i].top_space,
+                             row_info[i].top_space_default);
+               getTokenValue(line, "bottomspace", row_info[i].bottom_space,
+                             row_info[i].bottom_space_default);
+               getTokenValue(line, "interlinespace", row_info[i].interline_space,
+                             row_info[i].interline_space_default);
                getTokenValue(line, "endfirsthead", row_info[i].endfirsthead);
                getTokenValue(line, "endhead", row_info[i].endhead);
                getTokenValue(line, "endfoot", row_info[i].endfoot);
@@ -1339,19 +1435,20 @@ LyXTabular::cellstruct & LyXTabular::cellinfo_of_cell(idx_type cell) const
 
 
 void LyXTabular::setMultiColumn(Buffer * buffer, idx_type cell,
-                                idx_type number)
+                               idx_type number)
 {
        cellstruct & cs = cellinfo_of_cell(cell);
        cs.multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
        cs.alignment = column_info[column_of_cell(cell)].alignment;
        cs.top_line = row_info[row_of_cell(cell)].top_line;
        cs.bottom_line = row_info[row_of_cell(cell)].bottom_line;
+       cs.left_line = column_info[column_of_cell(cell)].left_line;
        cs.right_line = column_info[column_of_cell(cell+number-1)].right_line;
        for (idx_type i = 1; i < number; ++i) {
                cellstruct & cs1 = cellinfo_of_cell(cell + i);
                cs1.multicolumn = CELL_PART_OF_MULTICOLUMN;
                cs.inset->appendParagraphs(buffer, cs1.inset->paragraphs());
-               cs1.inset->clear(false);
+               cs1.inset->clear();
        }
        set_row_column_number_info();
 }
@@ -1396,6 +1493,18 @@ LyXTabular::idx_type LyXTabular::unsetMultiColumn(idx_type cell)
 }
 
 
+void LyXTabular::setBookTabs(bool what)
+{
+       use_booktabs = what;
+}
+
+
+bool LyXTabular::useBookTabs() const
+{
+       return use_booktabs;
+}
+
+
 void LyXTabular::setLongTabular(bool what)
 {
        is_long_tabular = what;
@@ -1489,10 +1598,10 @@ LyXTabular::idx_type LyXTabular::getLastCellBelow(idx_type cell) const
 
 
 LyXTabular::idx_type LyXTabular::getCellNumber(row_type row,
-                                               col_type column) const
+                                              col_type column) const
 {
        BOOST_ASSERT(column != npos && column < columns_ &&
-                    row    != npos && row    < rows_);
+                    row    != npos && row    < rows_);
        return cell_info[row][column].cellno;
 }
 
@@ -1518,7 +1627,7 @@ LyXTabular::BoxType LyXTabular::getUsebox(idx_type cell) const
 //  This are functions used for the longtable support
 ///
 void LyXTabular::setLTHead(row_type row, bool flag, ltType const & hd,
-                           bool first)
+                          bool first)
 {
        if (first) {
                endfirsthead = hd;
@@ -1549,7 +1658,7 @@ bool LyXTabular::getRowOfLTFirstHead(row_type row, ltType & hd) const
 
 
 void LyXTabular::setLTFoot(row_type row, bool flag, ltType const & fd,
-                           bool last)
+                          bool last)
 {
        if (last) {
                endlastfoot = fd;
@@ -1696,12 +1805,14 @@ int LyXTabular::TeXTopHLine(ostream & os, row_type row) const
                if (topLine(i))
                        ++tmp;
        }
-       if (tmp == n - fcell) {
-               os << "\\hline ";
+       if (use_booktabs && row == 0) {
+               os << "\\toprule ";
+       } else if (tmp == n - fcell) {
+               os << (use_booktabs ? "\\midrule " : "\\hline ");
        } else if (tmp) {
                for (idx_type i = fcell; i < n; ++i) {
                        if (topLine(i)) {
-                               os << "\\cline{"
+                               os << (use_booktabs ? "\\cmidrule{" : "\\cline{")
                                   << column_of_cell(i) + 1
                                   << '-'
                                   << right_column_of_cell(i) + 1
@@ -1730,12 +1841,14 @@ int LyXTabular::TeXBottomHLine(ostream & os, row_type row) const
                if (bottomLine(i))
                        ++tmp;
        }
-       if (tmp == n - fcell) {
-               os << "\\hline";
+       if (use_booktabs && row == rows_ - 1) {
+               os << "\\bottomrule";
+       } else if (tmp == n - fcell) {
+               os << (use_booktabs ? "\\midrule" : "\\hline");
        } else if (tmp) {
                for (idx_type i = fcell; i < n; ++i) {
                        if (bottomLine(i)) {
-                               os << "\\cline{"
+                               os << (use_booktabs ? "\\cmidrule{" : "\\cline{")
                                   << column_of_cell(i) + 1
                                   << '-'
                                   << right_column_of_cell(i) + 1
@@ -1966,8 +2079,23 @@ int LyXTabular::TeXRow(ostream & os, row_type i, Buffer const & buf,
                       OutputParams const & runparams) const
 {
        idx_type cell = getCellNumber(i, 0);
-
        int ret = TeXTopHLine(os, i);
+       if (row_info[i].top_space_default) {
+               if (use_booktabs)
+                       os << "\\addlinespace\n";
+               else
+                       os << "\\noalign{\\vskip\\doublerulesep}\n";
+       } else if(!row_info[i].top_space.zero()) {
+               if (use_booktabs)
+                       os << "\\addlinespace["
+                          << row_info[i].top_space.asLatexString() << "]\n";
+               else {
+                       os << "\\noalign{\\vskip"
+                          << row_info[i].top_space.asLatexString() << "}\n";
+               }
+               ++ret;
+       }
+       
        for (col_type j = 0; j < columns_; ++j) {
                if (isPartOfMultiColumn(i, j))
                        continue;
@@ -1992,9 +2120,36 @@ int LyXTabular::TeXRow(ostream & os, row_type i, Buffer const & buf,
                }
                ++cell;
        }
-       os << "\\tabularnewline\n";
+       os << "\\tabularnewline";
+       if (row_info[i].bottom_space_default) {
+               if (use_booktabs)
+                       os << "\\addlinespace";
+               else
+                       os << "[\\doublerulesep]";
+       } else if (!row_info[i].bottom_space.zero()) {
+               if (use_booktabs)
+                       os << "\\addlinespace";
+               os << '[' << row_info[i].bottom_space.asLatexString() << ']';
+       }
+       os << '\n';
        ++ret;
        ret += TeXBottomHLine(os, i);
+       if (row_info[i].interline_space_default) {
+               if (use_booktabs)
+                       os << "\\addlinespace\n";
+               else
+                       os << "\\noalign{\\vskip\\doublerulesep}\n";
+       } else if (!row_info[i].interline_space.zero()) {
+               if (use_booktabs)
+                       os << "\\addlinespace["
+                          << row_info[i].interline_space.asLatexString()
+                          << "]\n";
+               else
+                       os << "\\noalign{\\vskip"
+                          << row_info[i].interline_space.asLatexString()
+                          << "}\n";
+               ++ret;
+       }
        return ret;
 }
 
@@ -2020,7 +2175,7 @@ int LyXTabular::latex(Buffer const & buf, ostream & os,
                if (!column_info[i].align_special.empty()) {
                        os << column_info[i].align_special;
                } else {
-                       if (column_info[i].left_line)
+                       if (!use_booktabs && column_info[i].left_line)
                                os << '|';
                        if (!column_info[i].p_width.zero()) {
                                switch (column_info[i].alignment) {
@@ -2067,7 +2222,7 @@ int LyXTabular::latex(Buffer const & buf, ostream & os,
                                        break;
                                }
                        }
-                       if (column_info[i].right_line)
+                       if (!use_booktabs && column_info[i].right_line)
                                os << '|';
                }
        }
@@ -2107,48 +2262,6 @@ int LyXTabular::latex(Buffer const & buf, ostream & os,
 }
 
 
-int LyXTabular::linuxdoc(Buffer const & buf, ostream & os,
-                        const OutputParams & runparams) const
-{
-       os << "<tabular ca=\"";
-       for (col_type i = 0; i < columns_; ++i) {
-               switch (column_info[i].alignment) {
-               case LYX_ALIGN_LEFT:
-                       os << 'l';
-                       break;
-               case LYX_ALIGN_RIGHT:
-                       os << 'r';
-                       break;
-               default:
-                       os << 'c';
-                       break;
-               }
-       }
-       os << "\">\n";
-       idx_type cell = 0;
-       int ret = 0;
-       for (row_type i = 0; i < rows_; ++i) {
-               for (col_type j = 0; j < columns_; ++j) {
-                       if (isPartOfMultiColumn(i, j))
-                               continue;
-                       shared_ptr<InsetText> inset = getCellInset(cell);
-
-                       ret += inset->linuxdoc(buf, os, runparams);
-
-                       if (isLastCellInRow(cell)) {
-                               os << "@\n";
-                               ++ret;
-                       } else {
-                               os << "|";
-                       }
-                       ++cell;
-               }
-       }
-       os << "</tabular>\n";
-       return ret;
-}
-
-
 int LyXTabular::docbookRow(Buffer const & buf, ostream & os, row_type row,
                           OutputParams const & runparams) const
 {
@@ -2506,12 +2619,19 @@ shared_ptr<InsetText> LyXTabular::getCellInset(idx_type cell) const
 
 
 shared_ptr<InsetText> LyXTabular::getCellInset(row_type row,
-                                               col_type column) const
+                                              col_type column) const
 {
        return cell_info[row][column].inset;
 }
 
 
+void LyXTabular::setCellInset(row_type row, col_type column,
+                             shared_ptr<InsetText> ins) const
+{
+       cell_info[row][column].inset = ins;
+}
+
+
 LyXTabular::idx_type
 LyXTabular::getCellFromInset(InsetBase const * inset) const
 {
@@ -2540,6 +2660,8 @@ LyXTabular::getCellFromInset(InsetBase const * inset) const
 void LyXTabular::validate(LaTeXFeatures & features) const
 {
        features.require("NeedTabularnewline");
+       if (useBookTabs())
+               features.require("booktabs");
        if (isLongTabular())
                features.require("longtable");
        if (needRotating())
@@ -2553,15 +2675,6 @@ void LyXTabular::validate(LaTeXFeatures & features) const
 }
 
 
-void LyXTabular::getLabelList(Buffer const & buffer,
-                             std::vector<string> & list) const
-{
-       for (row_type i = 0; i < rows_; ++i)
-               for (col_type j = 0; j < columns_; ++j)
-                       getCellInset(i, j)->getLabelList(buffer, list);
-}
-
-
 LyXTabular::BoxType LyXTabular::useParbox(idx_type cell) const
 {
        ParagraphList const & parlist = getCellInset(cell)->paragraphs();