]> git.lyx.org Git - lyx.git/blob - src/tabular.C
remove LFUN_TOOLTIPS_TOGGLE and associated Dialog::tooltipsEnabled() method.
[lyx.git] / src / tabular.C
1 /**
2  * \file tabular.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Matthias Ettrich
8  * \author José Matos
9  * \author Jean-Marc Lasgouttes
10  * \author Angus Leeming
11  * \author John Levon
12  * \author André Pönitz
13  * \author Jürgen Vigna
14  *
15  * Full author contact details are available in file CREDITS.
16  */
17
18 #include <config.h>
19
20 #include "tabular.h"
21
22 #include "buffer.h"
23 #include "bufferparams.h"
24 #include "BufferView.h"
25 #include "cursor.h"
26 #include "debug.h"
27 #include "LaTeXFeatures.h"
28 #include "lyxlex.h"
29 #include "outputparams.h"
30 #include "paragraph.h"
31 #include "paragraph_funcs.h"
32
33 #include "insets/insettabular.h"
34
35 #include "support/lstrings.h"
36 #include "support/convert.h"
37
38 #include <sstream>
39
40 using lyx::support::ltrim;
41 using lyx::support::prefixIs;
42 using lyx::support::rtrim;
43 using lyx::support::suffixIs;
44
45 using boost::shared_ptr;
46 using boost::dynamic_pointer_cast;
47
48 using std::abs;
49 using std::endl;
50 using std::getline;
51 using std::max;
52
53 using std::istream;
54 using std::ostream;
55 using std::ostringstream;
56 using std::vector;
57 using std::string;
58
59 #ifndef CXX_GLOBAL_CSTD
60 using std::strlen;
61 #endif
62
63
64 namespace {
65
66 int const WIDTH_OF_LINE = 5;
67 int const default_line_space = 10;
68
69
70 template <class T>
71 string const write_attribute(string const & name, T const & t)
72 {
73         string const s = tostr(t);
74         return s.empty() ? s : " " + name + "=\"" + s + "\"";
75 }
76
77 template <>
78 string const write_attribute(string const & name, string const & t)
79 {
80         return t.empty() ? t : " " + name + "=\"" + t + "\"";
81 }
82
83
84 template <>
85 string const write_attribute(string const & name, bool const & b)
86 {
87         // we write only true attribute values so we remove a bit of the
88         // file format bloat for tabulars.
89         return b ? write_attribute(name, convert<string>(b)) : string();
90 }
91
92
93 template <>
94 string const write_attribute(string const & name, int const & i)
95 {
96         // we write only true attribute values so we remove a bit of the
97         // file format bloat for tabulars.
98         return i ? write_attribute(name, convert<string>(i)) : string();
99 }
100
101
102 template <>
103 string const write_attribute(string const & name, LyXTabular::idx_type const & i)
104 {
105         // we write only true attribute values so we remove a bit of the
106         // file format bloat for tabulars.
107         return i ? write_attribute(name, convert<string>(i)) : string();
108 }
109
110
111 template <>
112 string const write_attribute(string const & name, LyXLength const & value)
113 {
114         // we write only the value if we really have one same reson as above.
115         return value.zero() ? string() : write_attribute(name, value.asString());
116 }
117
118
119 string const tostr(LyXAlignment const & num)
120 {
121         switch (num) {
122         case LYX_ALIGN_NONE:
123                 return "none";
124         case LYX_ALIGN_BLOCK:
125                 return "block";
126         case LYX_ALIGN_LEFT:
127                 return "left";
128         case LYX_ALIGN_CENTER:
129                 return "center";
130         case LYX_ALIGN_RIGHT:
131                 return "right";
132         case LYX_ALIGN_LAYOUT:
133                 return "layout";
134         case LYX_ALIGN_SPECIAL:
135                 return "special";
136         }
137         return string();
138 }
139
140
141 string const tostr(LyXTabular::VAlignment const & num)
142 {
143         switch (num) {
144         case LyXTabular::LYX_VALIGN_TOP:
145                 return "top";
146         case LyXTabular::LYX_VALIGN_MIDDLE:
147                 return "middle";
148         case LyXTabular::LYX_VALIGN_BOTTOM:
149                 return "bottom";
150         }
151         return string();
152 }
153
154
155 string const tostr(LyXTabular::BoxType const & num)
156 {
157         switch (num) {
158         case LyXTabular::BOX_NONE:
159                 return "none";
160         case LyXTabular::BOX_PARBOX:
161                 return "parbox";
162         case LyXTabular::BOX_MINIPAGE:
163                 return "minipage";
164         }
165         return string();
166 }
167
168
169 // I would have liked a fromstr template a lot better. (Lgb)
170 bool string2type(string const str, LyXAlignment & num)
171 {
172         if (str == "none")
173                 num = LYX_ALIGN_NONE;
174         else if (str == "block")
175                 num = LYX_ALIGN_BLOCK;
176         else if (str == "left")
177                 num = LYX_ALIGN_LEFT;
178         else if (str == "center")
179                 num = LYX_ALIGN_CENTER;
180         else if (str == "right")
181                 num = LYX_ALIGN_RIGHT;
182         else
183                 return false;
184         return true;
185 }
186
187
188 bool string2type(string const str, LyXTabular::VAlignment & num)
189 {
190         if (str == "top")
191                 num = LyXTabular::LYX_VALIGN_TOP;
192         else if (str == "middle" )
193                 num = LyXTabular::LYX_VALIGN_MIDDLE;
194         else if (str == "bottom")
195                 num = LyXTabular::LYX_VALIGN_BOTTOM;
196         else
197                 return false;
198         return true;
199 }
200
201
202 bool string2type(string const str, LyXTabular::BoxType & num)
203 {
204         if (str == "none")
205                 num = LyXTabular::BOX_NONE;
206         else if (str == "parbox")
207                 num = LyXTabular::BOX_PARBOX;
208         else if (str == "minipage")
209                 num = LyXTabular::BOX_MINIPAGE;
210         else
211                 return false;
212         return true;
213 }
214
215
216 bool string2type(string const str, bool & num)
217 {
218         if (str == "true")
219                 num = true;
220         else if (str == "false")
221                 num = false;
222         else
223                 return false;
224         return true;
225 }
226
227
228 bool getTokenValue(string const & str, char const * token, string & ret)
229 {
230         ret.erase();
231         size_t token_length = strlen(token);
232         string::size_type pos = str.find(token);
233
234         if (pos == string::npos || pos + token_length + 1 >= str.length()
235                 || str[pos + token_length] != '=')
236                 return false;
237         pos += token_length + 1;
238         char ch = str[pos];
239         if (ch != '"' && ch != '\'') { // only read till next space
240                 ret += ch;
241                 ch = ' ';
242         }
243         while (pos < str.length() - 1 && str[++pos] != ch)
244                 ret += str[pos];
245
246         return true;
247 }
248
249
250 bool getTokenValue(string const & str, char const * token, int & num)
251 {
252         string tmp;
253         num = 0;
254         if (!getTokenValue(str, token, tmp))
255                 return false;
256         num = convert<int>(tmp);
257         return true;
258 }
259
260
261 bool getTokenValue(string const & str, char const * token, LyXAlignment & num)
262 {
263         string tmp;
264         return getTokenValue(str, token, tmp) && string2type(tmp, num);
265 }
266
267
268 bool getTokenValue(string const & str, char const * token,
269                                    LyXTabular::VAlignment & num)
270 {
271         string tmp;
272         return getTokenValue(str, token, tmp) && string2type(tmp, num);
273 }
274
275
276 bool getTokenValue(string const & str, char const * token,
277                                    LyXTabular::BoxType & num)
278 {
279         string tmp;
280         return getTokenValue(str, token, tmp) && string2type(tmp, num);
281 }
282
283
284 bool getTokenValue(string const & str, char const * token, bool & flag)
285 {
286         // set the flag always to false as this should be the default for bools
287         // not in the file-format.
288         flag = false;
289         string tmp;
290         return getTokenValue(str, token, tmp) && string2type(tmp, flag);
291 }
292
293
294 bool getTokenValue(string const & str, char const * token, LyXLength & len)
295 {
296         // set the lenght to be zero() as default as this it should be if not
297         // in the file format.
298         len = LyXLength();
299         string tmp;
300         return getTokenValue(str, token, tmp) && isValidLength(tmp, &len);
301 }
302
303
304 bool getTokenValue(string const & str, char const * token, LyXLength & len, bool & flag)
305 {
306         len = LyXLength();
307         flag = false;
308         string tmp;
309         if (!getTokenValue(str, token, tmp))
310                 return false;
311         if (tmp == "default") {
312                 flag = true;
313                 return  true;
314         }
315         return isValidLength(tmp, &len);
316 }
317
318
319 void l_getline(istream & is, string & str)
320 {
321         str.erase();
322         while (str.empty()) {
323                 getline(is, str);
324                 if (!str.empty() && str[str.length() - 1] == '\r')
325                         str.erase(str.length() - 1);
326         }
327 }
328
329 } // namespace
330
331 /// Define a few methods for the inner structs
332
333 LyXTabular::cellstruct::cellstruct(BufferParams const & bp)
334         : cellno(0),
335           width_of_cell(0),
336           multicolumn(LyXTabular::CELL_NORMAL),
337           alignment(LYX_ALIGN_CENTER),
338           valignment(LYX_VALIGN_TOP),
339           top_line(true),
340           bottom_line(false),
341           left_line(true),
342           right_line(false),
343           usebox(BOX_NONE),
344           rotate(false),
345           inset(new InsetText(bp))
346 {}
347
348
349 LyXTabular::cellstruct::cellstruct(cellstruct const & cs)
350         : cellno(cs.cellno),
351           width_of_cell(cs.width_of_cell),
352           multicolumn(cs.multicolumn),
353           alignment(cs.alignment),
354           valignment(cs.valignment),
355           top_line(cs.top_line),
356           bottom_line(cs.bottom_line),
357           left_line(cs.left_line),
358           right_line(cs.right_line),
359           usebox(cs.usebox),
360           rotate(cs.rotate),
361           align_special(cs.align_special),
362           p_width(cs.p_width),
363           inset(dynamic_cast<InsetText*>(cs.inset->clone().release()))
364 {}
365
366
367 LyXTabular::cellstruct &
368 LyXTabular::cellstruct::operator=(cellstruct cs)
369 {
370         swap(cs);
371         return *this;
372 }
373
374
375 void
376 LyXTabular::cellstruct::swap(cellstruct & rhs)
377 {
378         std::swap(cellno, rhs.cellno);
379         std::swap(width_of_cell, rhs.width_of_cell);
380         std::swap(multicolumn, rhs.multicolumn);
381         std::swap(alignment, rhs.alignment);
382         std::swap(valignment, rhs.valignment);
383         std::swap(top_line, rhs.top_line);
384         std::swap(bottom_line, rhs.bottom_line);
385         std::swap(left_line, rhs.left_line);
386         std::swap(right_line, rhs.right_line);
387         std::swap(usebox, rhs.usebox);
388         std::swap(rotate, rhs.rotate);
389         std::swap(align_special, rhs.align_special);
390         p_width.swap(rhs.p_width);
391         inset.swap(rhs.inset);
392 }
393
394
395 LyXTabular::rowstruct::rowstruct()
396         : ascent_of_row(0),
397           descent_of_row(0),
398           top_line(true),
399           bottom_line(false),
400           top_space_default(false),
401           bottom_space_default(false),
402           interline_space_default(false),
403           endhead(false),
404           endfirsthead(false),
405           endfoot(false),
406           endlastfoot(false),
407           newpage(false)
408 {}
409
410
411 LyXTabular::columnstruct::columnstruct()
412         : alignment(LYX_ALIGN_CENTER),
413           valignment(LYX_VALIGN_TOP),
414           left_line(true),
415           right_line(false),
416           width_of_column(0)
417 {
418 }
419
420
421 LyXTabular::ltType::ltType()
422         : topDL(false),
423           bottomDL(false),
424           empty(false)
425 {}
426
427
428 LyXTabular::LyXTabular(BufferParams const & bp, row_type rows_arg,
429                        col_type columns_arg)
430 {
431         init(bp, rows_arg, columns_arg);
432 }
433
434
435 // activates all lines and sets all widths to 0
436 void LyXTabular::init(BufferParams const & bp, row_type rows_arg,
437                       col_type columns_arg)
438 {
439         rows_    = rows_arg;
440         columns_ = columns_arg;
441         row_info = row_vector(rows_);
442         column_info = column_vector(columns_);
443         cell_info = cell_vvector(rows_, cell_vector(columns_, cellstruct(bp)));
444         row_info.reserve(10);
445         column_info.reserve(10);
446         cell_info.reserve(100);
447         fixCellNums();
448         for (row_type i = 0; i < rows_; ++i)
449                 cell_info[i].back().right_line = true;
450         row_info.back().bottom_line = true;
451         row_info.front().bottom_line = true;
452         column_info.back().right_line = true;
453         is_long_tabular = false;
454         rotate = false;
455         use_booktabs = false;
456 }
457
458
459 void LyXTabular::fixCellNums()
460 {
461         idx_type cellno = 0;
462         for (row_type i = 0; i < rows_; ++i) {
463                 for (col_type j = 0; j < columns_; ++j) {
464                         // When debugging it can be nice to set
465                         // this to true.
466                         cell_info[i][j].inset->setDrawFrame(false);
467                         cell_info[i][j].cellno = cellno++;
468                 }
469                 cell_info[i].back().right_line = true;
470         }
471
472         set_row_column_number_info();
473 }
474
475
476 void LyXTabular::appendRow(BufferParams const & bp, idx_type const cell)
477 {
478         ++rows_;
479
480         row_type const row = row_of_cell(cell);
481
482         row_vector::iterator rit = row_info.begin() + row;
483         row_info.insert(rit, rowstruct());
484         // now set the values of the row before
485         row_info[row] = row_info[row + 1];
486
487         cell_vvector old(rows_ - 1);
488         for (row_type i = 0; i < rows_ - 1; ++i)
489                 swap(cell_info[i], old[i]);
490
491         cell_info = cell_vvector(rows_, cell_vector(columns_, cellstruct(bp)));
492
493         for (row_type i = 0; i <= row; ++i)
494                 swap(cell_info[i], old[i]);
495         for (row_type i = row + 2; i < rows_; ++i)
496                 swap(cell_info[i], old[i - 1]);
497
498         if (bp.trackChanges)
499                 // FIXME: Change Tracking (MG)
500                 for (col_type j = 0; j < columns_; ++j)
501                         cell_info[row + 1][j].inset->markNew(true);
502
503         set_row_column_number_info();
504 }
505
506
507 void LyXTabular::deleteRow(row_type const row)
508 {
509         // Not allowed to delete last row
510         if (rows_ == 1)
511                 return;
512
513         row_info.erase(row_info.begin() + row);
514         cell_info.erase(cell_info.begin() + row);
515         --rows_;
516         fixCellNums();
517 }
518
519
520 void LyXTabular::copyRow(BufferParams const & bp, row_type const row)
521 {
522         ++rows_;
523
524         row_info.insert(row_info.begin() + row, row_info[row]);
525         cell_info.insert(cell_info.begin() + row, cell_info[row]);
526
527         if (bp.trackChanges)
528                 // FIXME: Change Tracking (MG)
529                 for (col_type j = 0; j < columns_; ++j)
530                         cell_info[row + 1][j].inset->markNew(true);
531
532         set_row_column_number_info();
533 }
534
535
536 void LyXTabular::appendColumn(BufferParams const & bp, idx_type const cell)
537 {
538         ++columns_;
539
540         col_type const column = column_of_cell(cell);
541         column_vector::iterator cit = column_info.begin() + column + 1;
542         column_info.insert(cit, columnstruct());
543         // set the column values of the column before
544         column_info[column + 1] = column_info[column];
545
546         for (row_type i = 0; i < rows_; ++i) {
547                 cell_info[i].insert(cell_info[i].begin() + column + 1, cellstruct(bp));
548
549                 // care about multicolumns
550                 if (cell_info[i][column + 1].multicolumn == CELL_BEGIN_OF_MULTICOLUMN)
551                         cell_info[i][column + 1].multicolumn = CELL_PART_OF_MULTICOLUMN;
552
553                 if (column + 2 >= columns_
554                     || cell_info[i][column + 2].multicolumn != CELL_PART_OF_MULTICOLUMN)
555                         cell_info[i][column + 1].multicolumn = LyXTabular::CELL_NORMAL;
556         }
557         //++column;
558         for (row_type i = 0; i < rows_; ++i) {
559                 cell_info[i][column + 1].inset->clear();
560                 if (bp.trackChanges)
561                         // FIXME: Change Tracking (MG)
562                         cell_info[i][column + 1].inset->markNew(true);
563         }
564         fixCellNums();
565 }
566
567
568 void LyXTabular::deleteColumn(col_type const column)
569 {
570         // Not allowed to delete last column
571         if (columns_ == 1)
572                 return;
573
574         column_info.erase(column_info.begin() + column);
575         for (row_type i = 0; i < rows_; ++i)
576                 cell_info[i].erase(cell_info[i].begin() + column);
577         --columns_;
578         fixCellNums();
579 }
580
581
582 void LyXTabular::copyColumn(BufferParams const & bp, col_type const column)
583 {
584         ++columns_;
585
586         column_info.insert(column_info.begin() + column, column_info[column]);
587
588         for (row_type i = 0; i < rows_; ++i)
589                 cell_info[i].insert(cell_info[i].begin() + column, cell_info[i][column]);
590
591         if (bp.trackChanges)
592                 // FIXME: Change Tracking (MG)
593                 for (row_type i = 0; i < rows_; ++i)
594                         cell_info[i][column + 1].inset->markNew(true);
595         fixCellNums();
596 }
597
598
599 void LyXTabular::set_row_column_number_info()
600 {
601         numberofcells = 0;
602         for (row_type row = 0; row < rows_; ++row) {
603                 for (col_type column = 0; column < columns_; ++column) {
604                         if (cell_info[row][column].multicolumn
605                                 != LyXTabular::CELL_PART_OF_MULTICOLUMN)
606                                 ++numberofcells;
607                         if (numberofcells == 0)
608                                 // FIXME: Is this intended?
609                                 cell_info[row][column].cellno = npos;
610                         else
611                                 cell_info[row][column].cellno =
612                                         numberofcells - 1;
613                 }
614         }
615
616         rowofcell.resize(numberofcells);
617         columnofcell.resize(numberofcells);
618
619         row_type row = 0;
620         col_type column = 0;
621         for (idx_type c = 0;
622                  c < numberofcells && row < rows_ && column < columns_;) {
623                 rowofcell[c] = row;
624                 columnofcell[c] = column;
625                 ++c;
626                 do {
627                         ++column;
628                 } while (column < columns_ &&
629                                  cell_info[row][column].multicolumn
630                                  == LyXTabular::CELL_PART_OF_MULTICOLUMN);
631
632                 if (column == columns_) {
633                         column = 0;
634                         ++row;
635                 }
636         }
637
638         for (row_type row = 0; row < rows_; ++row) {
639                 for (col_type column = 0; column < columns_; ++column) {
640                         if (isPartOfMultiColumn(row,column))
641                                 continue;
642                         cell_info[row][column].inset->setAutoBreakRows(
643                                 !getPWidth(getCellNumber(row, column)).zero());
644                 }
645         }
646 }
647
648
649 LyXTabular::idx_type LyXTabular::getNumberOfCells() const
650 {
651         return numberofcells;
652 }
653
654
655 LyXTabular::idx_type LyXTabular::numberOfCellsInRow(idx_type const cell) const
656 {
657         row_type const row = row_of_cell(cell);
658         idx_type result = 0;
659         for (col_type i = 0; i < columns_; ++i)
660                 if (cell_info[row][i].multicolumn != LyXTabular::CELL_PART_OF_MULTICOLUMN)
661                         ++result;
662         return result;
663 }
664
665
666 bool LyXTabular::topLine(idx_type const cell, bool const onlycolumn) const
667 {
668         if (!onlycolumn && isMultiColumn(cell) &&
669             !(use_booktabs && row_of_cell(cell) == 0))
670                 return cellinfo_of_cell(cell).top_line;
671         return row_info[row_of_cell(cell)].top_line;
672 }
673
674
675 bool LyXTabular::bottomLine(idx_type const cell, bool onlycolumn) const
676 {
677         if (!onlycolumn && isMultiColumn(cell) &&
678             !(use_booktabs && isLastRow(cell)))
679                 return cellinfo_of_cell(cell).bottom_line;
680         return row_info[row_of_cell(cell)].bottom_line;
681 }
682
683
684 bool LyXTabular::leftLine(idx_type cell, bool onlycolumn) const
685 {
686         if (use_booktabs)
687                 return false;
688         if (!onlycolumn && isMultiColumn(cell) &&
689                 (isFirstCellInRow(cell) || isMultiColumn(cell-1)))
690         {
691                 if (cellinfo_of_cell(cell).align_special.empty())
692                         return cellinfo_of_cell(cell).left_line;
693                 return prefixIs(ltrim(cellinfo_of_cell(cell).align_special), "|");
694         }
695         if (column_info[column_of_cell(cell)].align_special.empty())
696                 return column_info[column_of_cell(cell)].left_line;
697         return prefixIs(ltrim(column_info[column_of_cell(cell)].align_special), "|");
698 }
699
700
701 bool LyXTabular::rightLine(idx_type cell, bool onlycolumn) const
702 {
703         if (use_booktabs)
704                 return false;
705         if (!onlycolumn && isMultiColumn(cell) &&
706                 (isLastCellInRow(cell) || isMultiColumn(cell + 1)))
707         {
708                 if (cellinfo_of_cell(cell).align_special.empty())
709                         return cellinfo_of_cell(cell).right_line;
710                 return suffixIs(rtrim(cellinfo_of_cell(cell).align_special), "|");
711         }
712         if (column_info[column_of_cell(cell)].align_special.empty())
713                 return column_info[right_column_of_cell(cell)].right_line;
714         return suffixIs(rtrim(column_info[column_of_cell(cell)].align_special), "|");
715 }
716
717
718 bool LyXTabular::topAlreadyDrawn(idx_type cell) const
719 {
720         row_type row = row_of_cell(cell);
721         if (row > 0 && !getAdditionalHeight(row)) {
722                 col_type column = column_of_cell(cell);
723                 --row;
724                 while (column
725                            && cell_info[row][column].multicolumn
726                            == LyXTabular::CELL_PART_OF_MULTICOLUMN)
727                         --column;
728                 if (cell_info[row][column].multicolumn == LyXTabular::CELL_NORMAL)
729                         return row_info[row].bottom_line;
730                 else
731                         return cell_info[row][column].bottom_line;
732         }
733         return false;
734 }
735
736
737 bool LyXTabular::leftAlreadyDrawn(idx_type cell) const
738 {
739         col_type column = column_of_cell(cell);
740         if (column > 0) {
741                 row_type row = row_of_cell(cell);
742                 while (--column &&
743                            (cell_info[row][column].multicolumn ==
744                                 LyXTabular::CELL_PART_OF_MULTICOLUMN));
745                 if (getAdditionalWidth(cell_info[row][column].cellno))
746                         return false;
747                 return rightLine(cell_info[row][column].cellno);
748         }
749         return false;
750 }
751
752
753 bool LyXTabular::isLastRow(idx_type cell) const
754 {
755         return row_of_cell(cell) == rows_ - 1;
756 }
757
758
759 int LyXTabular::getAdditionalHeight(row_type row) const
760 {
761         if (!row || row >= rows_)
762                 return 0;
763
764         bool top = true;
765         bool bottom = true;
766
767         for (col_type column = 0; column < columns_ && bottom; ++column) {
768                 switch (cell_info[row - 1][column].multicolumn) {
769                 case LyXTabular::CELL_BEGIN_OF_MULTICOLUMN:
770                         bottom = cell_info[row - 1][column].bottom_line;
771                         break;
772                 case LyXTabular::CELL_NORMAL:
773                         bottom = row_info[row - 1].bottom_line;
774                 }
775         }
776         for (col_type column = 0; column < columns_ && top; ++column) {
777                 switch (cell_info[row][column].multicolumn) {
778                 case LyXTabular::CELL_BEGIN_OF_MULTICOLUMN:
779                         top = cell_info[row][column].top_line;
780                         break;
781                 case LyXTabular::CELL_NORMAL:
782                         top = row_info[row].top_line;
783                 }
784         }
785         int const interline_space = row_info[row - 1].interline_space_default ?
786                 default_line_space :
787                 row_info[row - 1].interline_space.inPixels(width_of_tabular);
788         if (top && bottom)
789                 return interline_space + WIDTH_OF_LINE;
790         return interline_space;
791 }
792
793
794 int LyXTabular::getAdditionalWidth(idx_type cell) const
795 {
796         // internally already set in setWidthOfCell
797         // used to get it back in text.C
798         col_type const col = right_column_of_cell(cell);
799         row_type const row = row_of_cell(cell);
800         if (col < columns_ - 1 && rightLine(cell) &&
801                 leftLine(cell_info[row][col+1].cellno)) // column_info[col+1].left_line)
802         {
803                 return WIDTH_OF_LINE;
804         }
805         return 0;
806 }
807
808
809 // returns the maximum over all rows
810 int LyXTabular::getWidthOfColumn(idx_type cell) const
811 {
812         col_type const column1 = column_of_cell(cell);
813         col_type const column2 = right_column_of_cell(cell);
814         int result = 0;
815         for (col_type i = column1; i <= column2; ++i)
816                 result += column_info[i].width_of_column;
817         return result;
818 }
819
820
821 int LyXTabular::getWidthOfTabular() const
822 {
823         return width_of_tabular;
824 }
825
826
827 // returns true if a complete update is necessary, otherwise false
828 bool LyXTabular::setWidthOfMulticolCell(idx_type cell, int new_width)
829 {
830         if (!isMultiColumn(cell))
831                 return false;
832
833         row_type const row = row_of_cell(cell);
834         col_type const column1 = column_of_cell(cell);
835         col_type const column2 = right_column_of_cell(cell);
836         int const old_val = cell_info[row][column2].width_of_cell;
837
838         // first set columns to 0 so we can calculate the right width
839         for (col_type i = column1; i <= column2; ++i) {
840                 cell_info[row][i].width_of_cell = 0;
841         }
842         // set the width to MAX_WIDTH until width > 0
843         int width = new_width + 2 * WIDTH_OF_LINE;
844         col_type i = column1;
845         for (; i < column2 && width > column_info[i].width_of_column; ++i) {
846                 cell_info[row][i].width_of_cell = column_info[i].width_of_column;
847                 width -= column_info[i].width_of_column;
848         }
849         if (width > 0) {
850                 cell_info[row][i].width_of_cell = width;
851         }
852         if (old_val != cell_info[row][column2].width_of_cell) {
853                 // in this case we have to recalculate all multicolumn cells which
854                 // have this column as one of theirs but not as last one
855                 calculate_width_of_column_NMC(i);
856                 recalculateMulticolumnsOfColumn(i);
857                 calculate_width_of_column(i);
858         }
859         return true;
860 }
861
862
863 void LyXTabular::recalculateMulticolumnsOfColumn(col_type column)
864 {
865         // the last column does not have to be recalculated because all
866         // multicolumns will have here there last multicolumn cell which
867         // always will have the whole rest of the width of the cell.
868         if (columns_ < 2 || column > (columns_ - 2))
869                 return;
870         for (row_type row = 0; row < rows_; ++row) {
871                 int mc = cell_info[row][column].multicolumn;
872                 int nmc = cell_info[row][column+1].multicolumn;
873                 // we only have to update multicolumns which do not have this
874                 // column as their last column!
875                 if (mc == CELL_BEGIN_OF_MULTICOLUMN ||
876                           (mc == CELL_PART_OF_MULTICOLUMN &&
877                            nmc == CELL_PART_OF_MULTICOLUMN))
878                 {
879                         idx_type const cellno = cell_info[row][column].cellno;
880                         setWidthOfMulticolCell(cellno,
881                                                getWidthOfCell(cellno) - 2 * WIDTH_OF_LINE);
882                 }
883         }
884 }
885
886
887 void LyXTabular::setWidthOfCell(idx_type cell, int new_width)
888 {
889         row_type const row = row_of_cell(cell);
890         col_type const column1 = column_of_cell(cell);
891         bool tmp = false;
892         int width = 0;
893         int add_width = 0;
894
895         if (rightLine(cell_info[row][column1].cellno, true) &&
896                 column1 < columns_ - 1 &&
897                 leftLine(cell_info[row][column1+1].cellno, true))
898         {
899                 add_width = WIDTH_OF_LINE;
900         }
901
902         if (getWidthOfCell(cell) == new_width + 2 * WIDTH_OF_LINE + add_width)
903                 return;
904
905         if (isMultiColumnReal(cell)) {
906                 tmp = setWidthOfMulticolCell(cell, new_width);
907         } else {
908                 width = new_width + 2 * WIDTH_OF_LINE + add_width;
909                 cell_info[row][column1].width_of_cell = width;
910                 tmp = calculate_width_of_column_NMC(column1);
911                 if (tmp)
912                         recalculateMulticolumnsOfColumn(column1);
913         }
914         if (tmp) {
915                 for (col_type i = 0; i < columns_; ++i)
916                         calculate_width_of_column(i);
917                 calculate_width_of_tabular();
918         }
919 }
920
921
922 void LyXTabular::setAlignment(idx_type cell, LyXAlignment align,
923                               bool onlycolumn)
924 {
925         if (!isMultiColumn(cell) || onlycolumn)
926                 column_info[column_of_cell(cell)].alignment = align;
927         if (!onlycolumn)
928                 cellinfo_of_cell(cell).alignment = align;
929 }
930
931
932 void LyXTabular::setVAlignment(idx_type cell, VAlignment align,
933                                bool onlycolumn)
934 {
935         if (!isMultiColumn(cell) || onlycolumn)
936                 column_info[column_of_cell(cell)].valignment = align;
937         if (!onlycolumn)
938                 cellinfo_of_cell(cell).valignment = align;
939 }
940
941
942 namespace {
943
944 /**
945  * Allow line and paragraph breaks for fixed width cells or disallow them,
946  * merge cell paragraphs and reset layout to standard for variable width
947  * cells.
948  */
949 void toggleFixedWidth(LCursor & cur, InsetText * inset, bool fixedWidth)
950 {
951         inset->setAutoBreakRows(fixedWidth);
952         if (fixedWidth)
953                 return;
954
955         // merge all paragraphs to one
956         BufferParams const & bp =
957                 inset->getText(0)->bv_owner->buffer()->params();
958         while (inset->paragraphs().size() > 1)
959                 mergeParagraph(bp, inset->paragraphs(), 0);
960
961         // reset layout
962         cur.push(*inset);
963         // undo information has already been recorded
964         inset->getText(0)->setLayout(0, cur.lastpit() + 1,
965                         bp.getLyXTextClass().defaultLayoutName());
966         cur.pop();
967 }
968
969 }
970
971
972 void LyXTabular::setColumnPWidth(LCursor & cur, idx_type cell,
973                 LyXLength const & width)
974 {
975         col_type const j = column_of_cell(cell);
976
977         column_info[j].p_width = width;
978         for (row_type i = 0; i < rows_; ++i) {
979                 idx_type const cell = getCellNumber(i, j);
980                 // because of multicolumns
981                 toggleFixedWidth(cur, getCellInset(cell).get(),
982                                  !getPWidth(cell).zero());
983         }
984         // cur paragraph can become invalid after paragraphs were merged
985         if (cur.pit() > cur.lastpit())
986                 cur.pit() = cur.lastpit();
987         // cur position can become invalid after newlines were removed
988         if (cur.pos() > cur.lastpos())
989                 cur.pos() = cur.lastpos();
990 }
991
992
993 bool LyXTabular::setMColumnPWidth(LCursor & cur, idx_type cell,
994                 LyXLength const & width)
995 {
996         if (!isMultiColumn(cell))
997                 return false;
998
999         cellinfo_of_cell(cell).p_width = width;
1000         toggleFixedWidth(cur, getCellInset(cell).get(), !width.zero());
1001         // cur paragraph can become invalid after paragraphs were merged
1002         if (cur.pit() > cur.lastpit())
1003                 cur.pit() = cur.lastpit();
1004         // cur position can become invalid after newlines were removed
1005         if (cur.pos() > cur.lastpos())
1006                 cur.pos() = cur.lastpos();
1007         return true;
1008 }
1009
1010
1011 void LyXTabular::setAlignSpecial(idx_type cell, string const & special,
1012                                  LyXTabular::Feature what)
1013 {
1014         if (what == SET_SPECIAL_MULTI)
1015                 cellinfo_of_cell(cell).align_special = special;
1016         else
1017                 column_info[column_of_cell(cell)].align_special = special;
1018 }
1019
1020
1021 void LyXTabular::setAllLines(idx_type cell, bool line)
1022 {
1023         setTopLine(cell, line);
1024         setBottomLine(cell, line);
1025         setRightLine(cell, line);
1026         setLeftLine(cell, line);
1027 }
1028
1029
1030 void LyXTabular::setTopLine(idx_type cell, bool line, bool onlycolumn)
1031 {
1032         row_type const row = row_of_cell(cell);
1033         if (onlycolumn || !isMultiColumn(cell))
1034                 row_info[row].top_line = line;
1035         else
1036                 cellinfo_of_cell(cell).top_line = line;
1037 }
1038
1039
1040 void LyXTabular::setBottomLine(idx_type cell, bool line, bool onlycolumn)
1041 {
1042         if (onlycolumn || !isMultiColumn(cell))
1043                 row_info[row_of_cell(cell)].bottom_line = line;
1044         else
1045                 cellinfo_of_cell(cell).bottom_line = line;
1046 }
1047
1048
1049 void LyXTabular::setLeftLine(idx_type cell, bool line, bool onlycolumn)
1050 {
1051         if (onlycolumn || !isMultiColumn(cell))
1052                 column_info[column_of_cell(cell)].left_line = line;
1053         else
1054                 cellinfo_of_cell(cell).left_line = line;
1055 }
1056
1057
1058 void LyXTabular::setRightLine(idx_type cell, bool line, bool onlycolumn)
1059 {
1060         if (onlycolumn || !isMultiColumn(cell))
1061                 column_info[right_column_of_cell(cell)].right_line = line;
1062         else
1063                 cellinfo_of_cell(cell).right_line = line;
1064 }
1065
1066
1067 LyXAlignment LyXTabular::getAlignment(idx_type cell, bool onlycolumn) const
1068 {
1069         if (!onlycolumn && isMultiColumn(cell))
1070                 return cellinfo_of_cell(cell).alignment;
1071         return column_info[column_of_cell(cell)].alignment;
1072 }
1073
1074
1075 LyXTabular::VAlignment
1076 LyXTabular::getVAlignment(idx_type cell, bool onlycolumn) const
1077 {
1078         if (!onlycolumn && isMultiColumn(cell))
1079                 return cellinfo_of_cell(cell).valignment;
1080         return column_info[column_of_cell(cell)].valignment;
1081 }
1082
1083
1084 LyXLength const LyXTabular::getPWidth(idx_type cell) const
1085 {
1086         if (isMultiColumn(cell))
1087                 return cellinfo_of_cell(cell).p_width;
1088         return column_info[column_of_cell(cell)].p_width;
1089 }
1090
1091
1092 LyXLength const LyXTabular::getColumnPWidth(idx_type cell) const
1093 {
1094         return column_info[column_of_cell(cell)].p_width;
1095 }
1096
1097
1098 LyXLength const LyXTabular::getMColumnPWidth(idx_type cell) const
1099 {
1100         if (isMultiColumn(cell))
1101                 return cellinfo_of_cell(cell).p_width;
1102         return LyXLength();
1103 }
1104
1105
1106 string const LyXTabular::getAlignSpecial(idx_type cell, int what) const
1107 {
1108         if (what == SET_SPECIAL_MULTI)
1109                 return cellinfo_of_cell(cell).align_special;
1110         return column_info[column_of_cell(cell)].align_special;
1111 }
1112
1113
1114 int LyXTabular::getWidthOfCell(idx_type cell) const
1115 {
1116         row_type const row = row_of_cell(cell);
1117         col_type const column1 = column_of_cell(cell);
1118         col_type const column2 = right_column_of_cell(cell);
1119         int result = 0;
1120         for (col_type i = column1; i <= column2; ++i)
1121                 result += cell_info[row][i].width_of_cell;
1122         return result;
1123 }
1124
1125
1126 int LyXTabular::getBeginningOfTextInCell(idx_type cell) const
1127 {
1128         int x = 0;
1129
1130         switch (getAlignment(cell)) {
1131         case LYX_ALIGN_CENTER:
1132                 x += (getWidthOfColumn(cell) - getWidthOfCell(cell)) / 2;
1133                 break;
1134         case LYX_ALIGN_RIGHT:
1135                 x += getWidthOfColumn(cell) - getWidthOfCell(cell);
1136                 // + getAdditionalWidth(cell);
1137                 break;
1138         default:
1139                 // LYX_ALIGN_LEFT: nothing :-)
1140                 break;
1141         }
1142
1143         // the LaTeX Way :-(
1144         x += WIDTH_OF_LINE;
1145         return x;
1146 }
1147
1148
1149 bool LyXTabular::isFirstCellInRow(idx_type cell) const
1150 {
1151         return column_of_cell(cell) == 0;
1152 }
1153
1154
1155 LyXTabular::idx_type LyXTabular::getFirstCellInRow(row_type row) const
1156 {
1157         if (row > rows_ - 1)
1158                 row = rows_ - 1;
1159         return cell_info[row][0].cellno;
1160 }
1161
1162
1163 bool LyXTabular::isLastCellInRow(idx_type cell) const
1164 {
1165         return right_column_of_cell(cell) == columns_ - 1;
1166 }
1167
1168
1169 LyXTabular::idx_type LyXTabular::getLastCellInRow(row_type row) const
1170 {
1171         if (row > rows_ - 1)
1172                 row = rows_ - 1;
1173         return cell_info[row][columns_-1].cellno;
1174 }
1175
1176
1177 void LyXTabular::calculate_width_of_column(col_type column)
1178 {
1179         int maximum = 0;
1180         for (row_type i = 0; i < rows_; ++i)
1181                 maximum = max(cell_info[i][column].width_of_cell, maximum);
1182         column_info[column].width_of_column = maximum;
1183 }
1184
1185
1186 //
1187 // Calculate the columns regarding ONLY the normal cells and if this
1188 // column is inside a multicolumn cell then use it only if its the last
1189 // column of this multicolumn cell as this gives an added width to the
1190 // column, all the rest should be adapted!
1191 //
1192 bool LyXTabular::calculate_width_of_column_NMC(col_type column)
1193 {
1194         int const old_column_width = column_info[column].width_of_column;
1195         int max = 0;
1196         for (row_type i = 0; i < rows_; ++i) {
1197                 idx_type cell = getCellNumber(i, column);
1198                 bool ismulti = isMultiColumnReal(cell);
1199                 if ((!ismulti || column == right_column_of_cell(cell)) &&
1200                         cell_info[i][column].width_of_cell > max)
1201                 {
1202                         max = cell_info[i][column].width_of_cell;
1203                 }
1204         }
1205         column_info[column].width_of_column = max;
1206         return column_info[column].width_of_column != old_column_width;
1207 }
1208
1209
1210 void LyXTabular::calculate_width_of_tabular()
1211 {
1212         width_of_tabular = 0;
1213         for (col_type i = 0; i < columns_; ++i)
1214                 width_of_tabular += column_info[i].width_of_column;
1215 }
1216
1217
1218 LyXTabular::row_type LyXTabular::row_of_cell(idx_type cell) const
1219 {
1220         if (cell >= numberofcells)
1221                 return rows_ - 1;
1222         if (cell == npos)
1223                 return 0;
1224         return rowofcell[cell];
1225 }
1226
1227
1228 LyXTabular::col_type LyXTabular::column_of_cell(idx_type cell) const
1229 {
1230         if (cell >= numberofcells)
1231                 return columns_ - 1;
1232         if (cell == npos)
1233                 return 0;
1234         return columnofcell[cell];
1235 }
1236
1237
1238 LyXTabular::col_type LyXTabular::right_column_of_cell(idx_type cell) const
1239 {
1240         row_type const row = row_of_cell(cell);
1241         col_type column = column_of_cell(cell);
1242         while (column < columns_ - 1 &&
1243                    cell_info[row][column + 1].multicolumn == LyXTabular::CELL_PART_OF_MULTICOLUMN)
1244                 ++column;
1245         return column;
1246 }
1247
1248
1249 void LyXTabular::write(Buffer const & buf, ostream & os) const
1250 {
1251         // header line
1252         os << "<lyxtabular"
1253            << write_attribute("version", 3)
1254            << write_attribute("rows", rows_)
1255            << write_attribute("columns", columns_)
1256            << ">\n";
1257         // global longtable options
1258         os << "<features"
1259            << write_attribute("rotate", rotate)
1260            << write_attribute("booktabs", use_booktabs)
1261            << write_attribute("islongtable", is_long_tabular)
1262            << write_attribute("firstHeadTopDL", endfirsthead.topDL)
1263            << write_attribute("firstHeadBottomDL", endfirsthead.bottomDL)
1264            << write_attribute("firstHeadEmpty", endfirsthead.empty)
1265            << write_attribute("headTopDL", endhead.topDL)
1266            << write_attribute("headBottomDL", endhead.bottomDL)
1267            << write_attribute("footTopDL", endfoot.topDL)
1268            << write_attribute("footBottomDL", endfoot.bottomDL)
1269            << write_attribute("lastFootTopDL", endlastfoot.topDL)
1270            << write_attribute("lastFootBottomDL", endlastfoot.bottomDL)
1271            << write_attribute("lastFootEmpty", endlastfoot.empty)
1272            << ">\n";
1273         for (col_type j = 0; j < columns_; ++j) {
1274                 os << "<column"
1275                    << write_attribute("alignment", column_info[j].alignment)
1276                    << write_attribute("valignment", column_info[j].valignment)
1277                    << write_attribute("leftline", column_info[j].left_line)
1278                    << write_attribute("rightline", column_info[j].right_line)
1279                    << write_attribute("width", column_info[j].p_width.asString())
1280                    << write_attribute("special", column_info[j].align_special)
1281                    << ">\n";
1282         }
1283         for (row_type i = 0; i < rows_; ++i) {
1284                 os << "<row"
1285                    << write_attribute("topline", row_info[i].top_line)
1286                    << write_attribute("bottomline", row_info[i].bottom_line);
1287                 static const string def("default");
1288                 if (row_info[i].top_space_default)
1289                         os << write_attribute("topspace", def);
1290                 else
1291                         os << write_attribute("topspace", row_info[i].top_space);
1292                 if (row_info[i].bottom_space_default)
1293                         os << write_attribute("bottomspace", def);
1294                 else
1295                         os << write_attribute("bottomspace", row_info[i].bottom_space);
1296                 if (row_info[i].interline_space_default)
1297                         os << write_attribute("interlinespace", def);
1298                 else
1299                         os << write_attribute("interlinespace", row_info[i].interline_space);
1300                 os << write_attribute("endhead", row_info[i].endhead)
1301                    << write_attribute("endfirsthead", row_info[i].endfirsthead)
1302                    << write_attribute("endfoot", row_info[i].endfoot)
1303                    << write_attribute("endlastfoot", row_info[i].endlastfoot)
1304                    << write_attribute("newpage", row_info[i].newpage)
1305                    << ">\n";
1306                 for (col_type j = 0; j < columns_; ++j) {
1307                         os << "<cell"
1308                            << write_attribute("multicolumn", cell_info[i][j].multicolumn)
1309                            << write_attribute("alignment", cell_info[i][j].alignment)
1310                            << write_attribute("valignment", cell_info[i][j].valignment)
1311                            << write_attribute("topline", cell_info[i][j].top_line)
1312                            << write_attribute("bottomline", cell_info[i][j].bottom_line)
1313                            << write_attribute("leftline", cell_info[i][j].left_line)
1314                            << write_attribute("rightline", cell_info[i][j].right_line)
1315                            << write_attribute("rotate", cell_info[i][j].rotate)
1316                            << write_attribute("usebox", cell_info[i][j].usebox)
1317                            << write_attribute("width", cell_info[i][j].p_width)
1318                            << write_attribute("special", cell_info[i][j].align_special)
1319                            << ">\n";
1320                         os << "\\begin_inset ";
1321                         cell_info[i][j].inset->write(buf, os);
1322                         os << "\n\\end_inset\n"
1323                            << "</cell>\n";
1324                 }
1325                 os << "</row>\n";
1326         }
1327         os << "</lyxtabular>\n";
1328 }
1329
1330
1331 void LyXTabular::read(Buffer const & buf, LyXLex & lex)
1332 {
1333         string line;
1334         istream & is = lex.getStream();
1335
1336         l_getline(is, line);
1337         if (!prefixIs(line, "<lyxtabular ")
1338                 && !prefixIs(line, "<LyXTabular ")) {
1339                 BOOST_ASSERT(false);
1340                 return;
1341         }
1342
1343         int version;
1344         if (!getTokenValue(line, "version", version))
1345                 return;
1346         BOOST_ASSERT(version >= 2);
1347
1348         int rows_arg;
1349         if (!getTokenValue(line, "rows", rows_arg))
1350                 return;
1351         int columns_arg;
1352         if (!getTokenValue(line, "columns", columns_arg))
1353                 return;
1354         init(buf.params(), rows_arg, columns_arg);
1355         l_getline(is, line);
1356         if (!prefixIs(line, "<features")) {
1357                 lyxerr << "Wrong tabular format (expected <features ...> got"
1358                        << line << ')' << endl;
1359                 return;
1360         }
1361         getTokenValue(line, "rotate", rotate);
1362         getTokenValue(line, "booktabs", use_booktabs);
1363         getTokenValue(line, "islongtable", is_long_tabular);
1364         getTokenValue(line, "firstHeadTopDL", endfirsthead.topDL);
1365         getTokenValue(line, "firstHeadBottomDL", endfirsthead.bottomDL);
1366         getTokenValue(line, "firstHeadEmpty", endfirsthead.empty);
1367         getTokenValue(line, "headTopDL", endhead.topDL);
1368         getTokenValue(line, "headBottomDL", endhead.bottomDL);
1369         getTokenValue(line, "footTopDL", endfoot.topDL);
1370         getTokenValue(line, "footBottomDL", endfoot.bottomDL);
1371         getTokenValue(line, "lastFootTopDL", endlastfoot.topDL);
1372         getTokenValue(line, "lastFootBottomDL", endlastfoot.bottomDL);
1373         getTokenValue(line, "lastFootEmpty", endlastfoot.empty);
1374
1375         for (col_type j = 0; j < columns_; ++j) {
1376                 l_getline(is,line);
1377                 if (!prefixIs(line,"<column")) {
1378                         lyxerr << "Wrong tabular format (expected <column ...> got"
1379                                << line << ')' << endl;
1380                         return;
1381                 }
1382                 getTokenValue(line, "alignment", column_info[j].alignment);
1383                 getTokenValue(line, "valignment", column_info[j].valignment);
1384                 getTokenValue(line, "leftline", column_info[j].left_line);
1385                 getTokenValue(line, "rightline", column_info[j].right_line);
1386                 getTokenValue(line, "width", column_info[j].p_width);
1387                 getTokenValue(line, "special", column_info[j].align_special);
1388         }
1389
1390         for (row_type i = 0; i < rows_; ++i) {
1391                 l_getline(is, line);
1392                 if (!prefixIs(line, "<row")) {
1393                         lyxerr << "Wrong tabular format (expected <row ...> got"
1394                                << line << ')' << endl;
1395                         return;
1396                 }
1397                 getTokenValue(line, "topline", row_info[i].top_line);
1398                 getTokenValue(line, "bottomline", row_info[i].bottom_line);
1399                 getTokenValue(line, "topspace", row_info[i].top_space,
1400                               row_info[i].top_space_default);
1401                 getTokenValue(line, "bottomspace", row_info[i].bottom_space,
1402                               row_info[i].bottom_space_default);
1403                 getTokenValue(line, "interlinespace", row_info[i].interline_space,
1404                               row_info[i].interline_space_default);
1405                 getTokenValue(line, "endfirsthead", row_info[i].endfirsthead);
1406                 getTokenValue(line, "endhead", row_info[i].endhead);
1407                 getTokenValue(line, "endfoot", row_info[i].endfoot);
1408                 getTokenValue(line, "endlastfoot", row_info[i].endlastfoot);
1409                 getTokenValue(line, "newpage", row_info[i].newpage);
1410                 for (col_type j = 0; j < columns_; ++j) {
1411                         l_getline(is, line);
1412                         if (!prefixIs(line, "<cell")) {
1413                                 lyxerr << "Wrong tabular format (expected <cell ...> got"
1414                                        << line << ')' << endl;
1415                                 return;
1416                         }
1417                         getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn);
1418                         getTokenValue(line, "alignment", cell_info[i][j].alignment);
1419                         getTokenValue(line, "valignment", cell_info[i][j].valignment);
1420                         getTokenValue(line, "topline", cell_info[i][j].top_line);
1421                         getTokenValue(line, "bottomline", cell_info[i][j].bottom_line);
1422                         getTokenValue(line, "leftline", cell_info[i][j].left_line);
1423                         getTokenValue(line, "rightline", cell_info[i][j].right_line);
1424                         getTokenValue(line, "rotate", cell_info[i][j].rotate);
1425                         getTokenValue(line, "usebox", cell_info[i][j].usebox);
1426                         getTokenValue(line, "width", cell_info[i][j].p_width);
1427                         getTokenValue(line, "special", cell_info[i][j].align_special);
1428                         l_getline(is, line);
1429                         if (prefixIs(line, "\\begin_inset")) {
1430                                 cell_info[i][j].inset->read(buf, lex);
1431                                 l_getline(is, line);
1432                         }
1433                         if (!prefixIs(line, "</cell>")) {
1434                                 lyxerr << "Wrong tabular format (expected </cell> got"
1435                                        << line << ')' << endl;
1436                                 return;
1437                         }
1438                 }
1439                 l_getline(is, line);
1440                 if (!prefixIs(line, "</row>")) {
1441                         lyxerr << "Wrong tabular format (expected </row> got"
1442                                << line << ')' << endl;
1443                         return;
1444                 }
1445         }
1446         while (!prefixIs(line, "</lyxtabular>")) {
1447                 l_getline(is, line);
1448         }
1449         set_row_column_number_info();
1450 }
1451
1452
1453 bool LyXTabular::isMultiColumn(idx_type cell) const
1454 {
1455         return cellinfo_of_cell(cell).multicolumn != LyXTabular::CELL_NORMAL;
1456 }
1457
1458
1459 bool LyXTabular::isMultiColumnReal(idx_type cell) const
1460 {
1461         return column_of_cell(cell) != right_column_of_cell(cell) &&
1462                         cellinfo_of_cell(cell).multicolumn != LyXTabular::CELL_NORMAL;
1463 }
1464
1465
1466 LyXTabular::cellstruct & LyXTabular::cellinfo_of_cell(idx_type cell) const
1467 {
1468         return cell_info[row_of_cell(cell)][column_of_cell(cell)];
1469 }
1470
1471
1472 void LyXTabular::setMultiColumn(Buffer * buffer, idx_type cell,
1473                                 idx_type number)
1474 {
1475         cellstruct & cs = cellinfo_of_cell(cell);
1476         cs.multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
1477         cs.alignment = column_info[column_of_cell(cell)].alignment;
1478         cs.top_line = row_info[row_of_cell(cell)].top_line;
1479         cs.bottom_line = row_info[row_of_cell(cell)].bottom_line;
1480         cs.left_line = column_info[column_of_cell(cell)].left_line;
1481         cs.right_line = column_info[column_of_cell(cell+number-1)].right_line;
1482         for (idx_type i = 1; i < number; ++i) {
1483                 cellstruct & cs1 = cellinfo_of_cell(cell + i);
1484                 cs1.multicolumn = CELL_PART_OF_MULTICOLUMN;
1485                 cs.inset->appendParagraphs(buffer, cs1.inset->paragraphs());
1486                 cs1.inset->clear();
1487         }
1488         set_row_column_number_info();
1489 }
1490
1491
1492 LyXTabular::idx_type LyXTabular::cells_in_multicolumn(idx_type cell) const
1493 {
1494         row_type const row = row_of_cell(cell);
1495         col_type column = column_of_cell(cell);
1496         idx_type result = 1;
1497         ++column;
1498         while (column < columns_ &&
1499                    cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN)
1500         {
1501                 ++result;
1502                 ++column;
1503         }
1504         return result;
1505 }
1506
1507
1508 LyXTabular::idx_type LyXTabular::unsetMultiColumn(idx_type cell)
1509 {
1510         row_type const row = row_of_cell(cell);
1511         col_type column = column_of_cell(cell);
1512
1513         idx_type result = 0;
1514
1515         if (cell_info[row][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) {
1516                 cell_info[row][column].multicolumn = CELL_NORMAL;
1517                 ++column;
1518                 while (column < columns_ &&
1519                            cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN)
1520                 {
1521                         cell_info[row][column].multicolumn = CELL_NORMAL;
1522                         ++column;
1523                         ++result;
1524                 }
1525         }
1526         set_row_column_number_info();
1527         return result;
1528 }
1529
1530
1531 void LyXTabular::setBookTabs(bool what)
1532 {
1533         use_booktabs = what;
1534 }
1535
1536
1537 bool LyXTabular::useBookTabs() const
1538 {
1539         return use_booktabs;
1540 }
1541
1542
1543 void LyXTabular::setLongTabular(bool what)
1544 {
1545         is_long_tabular = what;
1546 }
1547
1548
1549 bool LyXTabular::isLongTabular() const
1550 {
1551         return is_long_tabular;
1552 }
1553
1554
1555 void LyXTabular::setRotateTabular(bool flag)
1556 {
1557         rotate = flag;
1558 }
1559
1560
1561 bool LyXTabular::getRotateTabular() const
1562 {
1563         return rotate;
1564 }
1565
1566
1567 void LyXTabular::setRotateCell(idx_type cell, bool flag)
1568 {
1569         cellinfo_of_cell(cell).rotate = flag;
1570 }
1571
1572
1573 bool LyXTabular::getRotateCell(idx_type cell) const
1574 {
1575         return cellinfo_of_cell(cell).rotate;
1576 }
1577
1578
1579 bool LyXTabular::needRotating() const
1580 {
1581         if (rotate)
1582                 return true;
1583         for (row_type i = 0; i < rows_; ++i)
1584                 for (col_type j = 0; j < columns_; ++j)
1585                         if (cell_info[i][j].rotate)
1586                                 return true;
1587         return false;
1588 }
1589
1590
1591 bool LyXTabular::isLastCell(idx_type cell) const
1592 {
1593         if (cell + 1 < numberofcells)
1594                 return false;
1595         return true;
1596 }
1597
1598
1599 LyXTabular::idx_type LyXTabular::getCellAbove(idx_type cell) const
1600 {
1601         if (row_of_cell(cell) > 0)
1602                 return cell_info[row_of_cell(cell)-1][column_of_cell(cell)].cellno;
1603         return cell;
1604 }
1605
1606
1607 LyXTabular::idx_type LyXTabular::getCellBelow(idx_type cell) const
1608 {
1609         if (row_of_cell(cell) + 1 < rows_)
1610                 return cell_info[row_of_cell(cell)+1][column_of_cell(cell)].cellno;
1611         return cell;
1612 }
1613
1614
1615 LyXTabular::idx_type LyXTabular::getLastCellAbove(idx_type cell) const
1616 {
1617         if (row_of_cell(cell) == 0)
1618                 return cell;
1619         if (!isMultiColumn(cell))
1620                 return getCellAbove(cell);
1621         return cell_info[row_of_cell(cell) - 1][right_column_of_cell(cell)].cellno;
1622 }
1623
1624
1625 LyXTabular::idx_type LyXTabular::getLastCellBelow(idx_type cell) const
1626 {
1627         if (row_of_cell(cell) + 1 >= rows_)
1628                 return cell;
1629         if (!isMultiColumn(cell))
1630                 return getCellBelow(cell);
1631         return cell_info[row_of_cell(cell) + 1][right_column_of_cell(cell)].cellno;
1632 }
1633
1634
1635 LyXTabular::idx_type LyXTabular::getCellNumber(row_type row,
1636                                                col_type column) const
1637 {
1638         BOOST_ASSERT(column != npos && column < columns_ &&
1639                      row    != npos && row    < rows_);
1640         return cell_info[row][column].cellno;
1641 }
1642
1643
1644 void LyXTabular::setUsebox(idx_type cell, BoxType type)
1645 {
1646         cellinfo_of_cell(cell).usebox = type;
1647 }
1648
1649
1650 LyXTabular::BoxType LyXTabular::getUsebox(idx_type cell) const
1651 {
1652         if (column_info[column_of_cell(cell)].p_width.zero() &&
1653                 !(isMultiColumn(cell) && !cellinfo_of_cell(cell).p_width.zero()))
1654                 return BOX_NONE;
1655         if (cellinfo_of_cell(cell).usebox > 1)
1656                 return cellinfo_of_cell(cell).usebox;
1657         return useParbox(cell);
1658 }
1659
1660
1661 ///
1662 //  This are functions used for the longtable support
1663 ///
1664 void LyXTabular::setLTHead(row_type row, bool flag, ltType const & hd,
1665                            bool first)
1666 {
1667         if (first) {
1668                 endfirsthead = hd;
1669                 if (hd.set)
1670                         row_info[row].endfirsthead = flag;
1671         } else {
1672                 endhead = hd;
1673                 if (hd.set)
1674                         row_info[row].endhead = flag;
1675         }
1676 }
1677
1678
1679 bool LyXTabular::getRowOfLTHead(row_type row, ltType & hd) const
1680 {
1681         hd = endhead;
1682         hd.set = haveLTHead();
1683         return row_info[row].endhead;
1684 }
1685
1686
1687 bool LyXTabular::getRowOfLTFirstHead(row_type row, ltType & hd) const
1688 {
1689         hd = endfirsthead;
1690         hd.set = haveLTFirstHead();
1691         return row_info[row].endfirsthead;
1692 }
1693
1694
1695 void LyXTabular::setLTFoot(row_type row, bool flag, ltType const & fd,
1696                            bool last)
1697 {
1698         if (last) {
1699                 endlastfoot = fd;
1700                 if (fd.set)
1701                         row_info[row].endlastfoot = flag;
1702         } else {
1703                 endfoot = fd;
1704                 if (fd.set)
1705                         row_info[row].endfoot = flag;
1706         }
1707 }
1708
1709
1710 bool LyXTabular::getRowOfLTFoot(row_type row, ltType & fd) const
1711 {
1712         fd = endfoot;
1713         fd.set = haveLTFoot();
1714         return row_info[row].endfoot;
1715 }
1716
1717
1718 bool LyXTabular::getRowOfLTLastFoot(row_type row, ltType & fd) const
1719 {
1720         fd = endlastfoot;
1721         fd.set = haveLTLastFoot();
1722         return row_info[row].endlastfoot;
1723 }
1724
1725
1726 void LyXTabular::setLTNewPage(row_type row, bool what)
1727 {
1728         row_info[row].newpage = what;
1729 }
1730
1731
1732 bool LyXTabular::getLTNewPage(row_type row) const
1733 {
1734         return row_info[row].newpage;
1735 }
1736
1737
1738 bool LyXTabular::haveLTHead() const
1739 {
1740         for (row_type i = 0; i < rows_; ++i)
1741                 if (row_info[i].endhead)
1742                         return true;
1743         return false;
1744 }
1745
1746
1747 bool LyXTabular::haveLTFirstHead() const
1748 {
1749         if (endfirsthead.empty)
1750                 return false;
1751         for (row_type i = 0; i < rows_; ++i)
1752                 if (row_info[i].endfirsthead)
1753                         return true;
1754         return false;
1755 }
1756
1757
1758 bool LyXTabular::haveLTFoot() const
1759 {
1760         for (row_type i = 0; i < rows_; ++i)
1761                 if (row_info[i].endfoot)
1762                         return true;
1763         return false;
1764 }
1765
1766
1767 bool LyXTabular::haveLTLastFoot() const
1768 {
1769         if (endlastfoot.empty)
1770                 return false;
1771         for (row_type i = 0; i < rows_; ++i)
1772                 if (row_info[i].endlastfoot)
1773                         return true;
1774         return false;
1775 }
1776
1777
1778 // end longtable support functions
1779
1780 void LyXTabular::setAscentOfRow(row_type row, int height)
1781 {
1782         if (row >= rows_ || row_info[row].ascent_of_row == height)
1783                 return;
1784         row_info[row].ascent_of_row = height;
1785 }
1786
1787
1788 void LyXTabular::setDescentOfRow(row_type row, int height)
1789 {
1790         if (row >= rows_ || row_info[row].descent_of_row == height)
1791                 return;
1792         row_info[row].descent_of_row = height;
1793 }
1794
1795
1796 int LyXTabular::getAscentOfRow(row_type row) const
1797 {
1798         if (row >= rows_)
1799                 return 0;
1800         return row_info[row].ascent_of_row;
1801 }
1802
1803
1804 int LyXTabular::getDescentOfRow(row_type row) const
1805 {
1806         BOOST_ASSERT(row < rows_);
1807         return row_info[row].descent_of_row;
1808 }
1809
1810
1811 int LyXTabular::getHeightOfTabular() const
1812 {
1813         int height = 0;
1814         for (row_type row = 0; row < rows_; ++row)
1815                 height += getAscentOfRow(row) + getDescentOfRow(row) +
1816                         getAdditionalHeight(row);
1817         return height;
1818 }
1819
1820
1821 bool LyXTabular::isPartOfMultiColumn(row_type row, col_type column) const
1822 {
1823         BOOST_ASSERT(row < rows_);
1824         BOOST_ASSERT(column < columns_);
1825         return cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN;
1826 }
1827
1828
1829 int LyXTabular::TeXTopHLine(ostream & os, row_type row) const
1830 {
1831         // FIXME: assert or return 0 as in TeXBottomHLine()?
1832         BOOST_ASSERT(row != npos);
1833         BOOST_ASSERT(row < rows_);
1834
1835         idx_type const fcell = getFirstCellInRow(row);
1836         idx_type const n = numberOfCellsInRow(fcell) + fcell;
1837         idx_type tmp = 0;
1838
1839         for (idx_type i = fcell; i < n; ++i) {
1840                 if (topLine(i))
1841                         ++tmp;
1842         }
1843         if (use_booktabs && row == 0) {
1844                 os << "\\toprule ";
1845         } else if (tmp == n - fcell) {
1846                 os << (use_booktabs ? "\\midrule " : "\\hline ");
1847         } else if (tmp) {
1848                 for (idx_type i = fcell; i < n; ++i) {
1849                         if (topLine(i)) {
1850                                 os << (use_booktabs ? "\\cmidrule{" : "\\cline{")
1851                                    << column_of_cell(i) + 1
1852                                    << '-'
1853                                    << right_column_of_cell(i) + 1
1854                                    << "} ";
1855                         }
1856                 }
1857         } else {
1858                 return 0;
1859         }
1860         os << "\n";
1861         return 1;
1862 }
1863
1864
1865 int LyXTabular::TeXBottomHLine(ostream & os, row_type row) const
1866 {
1867         // FIXME: return 0 or assert as in TeXTopHLine()?
1868         if (row == npos || row >= rows_)
1869                 return 0;
1870
1871         idx_type const fcell = getFirstCellInRow(row);
1872         idx_type const n = numberOfCellsInRow(fcell) + fcell;
1873         idx_type tmp = 0;
1874
1875         for (idx_type i = fcell; i < n; ++i) {
1876                 if (bottomLine(i))
1877                         ++tmp;
1878         }
1879         if (use_booktabs && row == rows_ - 1) {
1880                 os << "\\bottomrule";
1881         } else if (tmp == n - fcell) {
1882                 os << (use_booktabs ? "\\midrule" : "\\hline");
1883         } else if (tmp) {
1884                 for (idx_type i = fcell; i < n; ++i) {
1885                         if (bottomLine(i)) {
1886                                 os << (use_booktabs ? "\\cmidrule{" : "\\cline{")
1887                                    << column_of_cell(i) + 1
1888                                    << '-'
1889                                    << right_column_of_cell(i) + 1
1890                                    << "} ";
1891                         }
1892                 }
1893         } else {
1894                 return 0;
1895         }
1896         os << "\n";
1897         return 1;
1898 }
1899
1900
1901 int LyXTabular::TeXCellPreamble(ostream & os, idx_type cell) const
1902 {
1903         int ret = 0;
1904
1905         if (getRotateCell(cell)) {
1906                 os << "\\begin{sideways}\n";
1907                 ++ret;
1908         }
1909         if (isMultiColumn(cell)) {
1910                 os << "\\multicolumn{" << cells_in_multicolumn(cell) << "}{";
1911                 if (!cellinfo_of_cell(cell).align_special.empty()) {
1912                         os << cellinfo_of_cell(cell).align_special << "}{";
1913                 } else {
1914                         if (leftLine(cell) &&
1915                                 (isFirstCellInRow(cell) ||
1916                                  (!isMultiColumn(cell - 1) && !leftLine(cell, true) &&
1917                                   !rightLine(cell - 1, true))))
1918                         {
1919                                 os << '|';
1920                         }
1921                         if (!getPWidth(cell).zero()) {
1922                                 switch (getVAlignment(cell)) {
1923                                 case LYX_VALIGN_TOP:
1924                                         os << 'p';
1925                                         break;
1926                                 case LYX_VALIGN_MIDDLE:
1927                                         os << 'm';
1928                                         break;
1929                                 case LYX_VALIGN_BOTTOM:
1930                                         os << 'b';
1931                                         break;
1932                                 }
1933                                 os << '{'
1934                                    << getPWidth(cell).asLatexString()
1935                                    << '}';
1936                         } else {
1937                                 switch (getAlignment(cell)) {
1938                                 case LYX_ALIGN_LEFT:
1939                                         os << 'l';
1940                                         break;
1941                                 case LYX_ALIGN_RIGHT:
1942                                         os << 'r';
1943                                         break;
1944                                 default:
1945                                         os << 'c';
1946                                         break;
1947                                 }
1948                         }
1949                         if (rightLine(cell))
1950                                 os << '|';
1951                         if (((cell + 1) < numberofcells) && !isFirstCellInRow(cell+1) &&
1952                                 leftLine(cell+1))
1953                                 os << '|';
1954                         os << "}{";
1955                 }
1956         }
1957         if (getUsebox(cell) == BOX_PARBOX) {
1958                 os << "\\parbox[";
1959                 switch (getVAlignment(cell)) {
1960                 case LYX_VALIGN_TOP:
1961                         os << 't';
1962                         break;
1963                 case LYX_VALIGN_MIDDLE:
1964                         os << 'c';
1965                         break;
1966                 case LYX_VALIGN_BOTTOM:
1967                         os << 'b';
1968                         break;
1969                 }
1970                 os << "]{" << getPWidth(cell).asLatexString() << "}{";
1971         } else if (getUsebox(cell) == BOX_MINIPAGE) {
1972                 os << "\\begin{minipage}[";
1973                 switch (getVAlignment(cell)) {
1974                 case LYX_VALIGN_TOP:
1975                         os << 't';
1976                         break;
1977                 case LYX_VALIGN_MIDDLE:
1978                         os << 'm';
1979                         break;
1980                 case LYX_VALIGN_BOTTOM:
1981                         os << 'b';
1982                         break;
1983                 }
1984                 os << "]{" << getPWidth(cell).asLatexString() << "}\n";
1985                 ++ret;
1986         }
1987         return ret;
1988 }
1989
1990
1991 int LyXTabular::TeXCellPostamble(ostream & os, idx_type cell) const
1992 {
1993         int ret = 0;
1994
1995         // usual cells
1996         if (getUsebox(cell) == BOX_PARBOX)
1997                 os << '}';
1998         else if (getUsebox(cell) == BOX_MINIPAGE) {
1999                 os << "%\n\\end{minipage}";
2000                 ret += 2;
2001         }
2002         if (isMultiColumn(cell)) {
2003                 os << '}';
2004         }
2005         if (getRotateCell(cell)) {
2006                 os << "%\n\\end{sideways}";
2007                 ++ret;
2008         }
2009         return ret;
2010 }
2011
2012
2013 int LyXTabular::TeXLongtableHeaderFooter(ostream & os, Buffer const & buf,
2014                                          OutputParams const & runparams) const
2015 {
2016         if (!is_long_tabular)
2017                 return 0;
2018
2019         int ret = 0;
2020         // output header info
2021         if (haveLTHead()) {
2022                 if (endhead.topDL) {
2023                         os << "\\hline\n";
2024                         ++ret;
2025                 }
2026                 for (row_type i = 0; i < rows_; ++i) {
2027                         if (row_info[i].endhead) {
2028                                 ret += TeXRow(os, i, buf, runparams);
2029                         }
2030                 }
2031                 if (endhead.bottomDL) {
2032                         os << "\\hline\n";
2033                         ++ret;
2034                 }
2035                 os << "\\endhead\n";
2036                 ++ret;
2037                 if (endfirsthead.empty) {
2038                         os << "\\endfirsthead\n";
2039                         ++ret;
2040                 }
2041         }
2042         // output firstheader info
2043         if (haveLTFirstHead()) {
2044                 if (endfirsthead.topDL) {
2045                         os << "\\hline\n";
2046                         ++ret;
2047                 }
2048                 for (row_type i = 0; i < rows_; ++i) {
2049                         if (row_info[i].endfirsthead) {
2050                                 ret += TeXRow(os, i, buf, runparams);
2051                         }
2052                 }
2053                 if (endfirsthead.bottomDL) {
2054                         os << "\\hline\n";
2055                         ++ret;
2056                 }
2057                 os << "\\endfirsthead\n";
2058                 ++ret;
2059         }
2060         // output footer info
2061         if (haveLTFoot()) {
2062                 if (endfoot.topDL) {
2063                         os << "\\hline\n";
2064                         ++ret;
2065                 }
2066                 for (row_type i = 0; i < rows_; ++i) {
2067                         if (row_info[i].endfoot) {
2068                                 ret += TeXRow(os, i, buf, runparams);
2069                         }
2070                 }
2071                 if (endfoot.bottomDL) {
2072                         os << "\\hline\n";
2073                         ++ret;
2074                 }
2075                 os << "\\endfoot\n";
2076                 ++ret;
2077                 if (endlastfoot.empty) {
2078                         os << "\\endlastfoot\n";
2079                         ++ret;
2080                 }
2081         }
2082         // output lastfooter info
2083         if (haveLTLastFoot()) {
2084                 if (endlastfoot.topDL) {
2085                         os << "\\hline\n";
2086                         ++ret;
2087                 }
2088                 for (row_type i = 0; i < rows_; ++i) {
2089                         if (row_info[i].endlastfoot) {
2090                                 ret += TeXRow(os, i, buf, runparams);
2091                         }
2092                 }
2093                 if (endlastfoot.bottomDL) {
2094                         os << "\\hline\n";
2095                         ++ret;
2096                 }
2097                 os << "\\endlastfoot\n";
2098                 ++ret;
2099         }
2100         return ret;
2101 }
2102
2103
2104 bool LyXTabular::isValidRow(row_type row) const
2105 {
2106         if (!is_long_tabular)
2107                 return true;
2108         return !row_info[row].endhead && !row_info[row].endfirsthead &&
2109                         !row_info[row].endfoot && !row_info[row].endlastfoot;
2110 }
2111
2112
2113 int LyXTabular::TeXRow(ostream & os, row_type i, Buffer const & buf,
2114                        OutputParams const & runparams) const
2115 {
2116         idx_type cell = getCellNumber(i, 0);
2117         int ret = TeXTopHLine(os, i);
2118         if (row_info[i].top_space_default) {
2119                 if (use_booktabs)
2120                         os << "\\addlinespace\n";
2121                 else
2122                         os << "\\noalign{\\vskip\\doublerulesep}\n";
2123         } else if(!row_info[i].top_space.zero()) {
2124                 if (use_booktabs)
2125                         os << "\\addlinespace["
2126                            << row_info[i].top_space.asLatexString() << "]\n";
2127                 else {
2128                         os << "\\noalign{\\vskip"
2129                            << row_info[i].top_space.asLatexString() << "}\n";
2130                 }
2131                 ++ret;
2132         }
2133         
2134         for (col_type j = 0; j < columns_; ++j) {
2135                 if (isPartOfMultiColumn(i, j))
2136                         continue;
2137                 ret += TeXCellPreamble(os, cell);
2138                 shared_ptr<InsetText> inset = getCellInset(cell);
2139
2140                 Paragraph const & par = inset->paragraphs().front();
2141                 bool rtl = par.isRightToLeftPar(buf.params())
2142                         && !par.empty()
2143                         && getPWidth(cell).zero();
2144
2145                 if (rtl)
2146                         os << "\\R{";
2147                 ret += inset->latex(buf, os, runparams);
2148                 if (rtl)
2149                         os << '}';
2150
2151                 ret += TeXCellPostamble(os, cell);
2152                 if (!isLastCellInRow(cell)) { // not last cell in row
2153                         os << "&\n";
2154                         ++ret;
2155                 }
2156                 ++cell;
2157         }
2158         os << "\\tabularnewline";
2159         if (row_info[i].bottom_space_default) {
2160                 if (use_booktabs)
2161                         os << "\\addlinespace";
2162                 else
2163                         os << "[\\doublerulesep]";
2164         } else if (!row_info[i].bottom_space.zero()) {
2165                 if (use_booktabs)
2166                         os << "\\addlinespace";
2167                 os << '[' << row_info[i].bottom_space.asLatexString() << ']';
2168         }
2169         os << '\n';
2170         ++ret;
2171         ret += TeXBottomHLine(os, i);
2172         if (row_info[i].interline_space_default) {
2173                 if (use_booktabs)
2174                         os << "\\addlinespace\n";
2175                 else
2176                         os << "\\noalign{\\vskip\\doublerulesep}\n";
2177         } else if (!row_info[i].interline_space.zero()) {
2178                 if (use_booktabs)
2179                         os << "\\addlinespace["
2180                            << row_info[i].interline_space.asLatexString()
2181                            << "]\n";
2182                 else
2183                         os << "\\noalign{\\vskip"
2184                            << row_info[i].interline_space.asLatexString()
2185                            << "}\n";
2186                 ++ret;
2187         }
2188         return ret;
2189 }
2190
2191
2192 int LyXTabular::latex(Buffer const & buf, ostream & os,
2193                       OutputParams const & runparams) const
2194 {
2195         int ret = 0;
2196
2197         //+---------------------------------------------------------------------
2198         //+                      first the opening preamble                    +
2199         //+---------------------------------------------------------------------
2200
2201         if (rotate) {
2202                 os << "\\begin{sideways}\n";
2203                 ++ret;
2204         }
2205         if (is_long_tabular)
2206                 os << "\\begin{longtable}{";
2207         else
2208                 os << "\\begin{tabular}{";
2209         for (col_type i = 0; i < columns_; ++i) {
2210                 if (!column_info[i].align_special.empty()) {
2211                         os << column_info[i].align_special;
2212                 } else {
2213                         if (!use_booktabs && column_info[i].left_line)
2214                                 os << '|';
2215                         if (!column_info[i].p_width.zero()) {
2216                                 switch (column_info[i].alignment) {
2217                                 case LYX_ALIGN_LEFT:
2218                                         os << ">{\\raggedright}";
2219                                         break;
2220                                 case LYX_ALIGN_RIGHT:
2221                                         os << ">{\\raggedleft}";
2222                                         break;
2223                                 case LYX_ALIGN_CENTER:
2224                                         os << ">{\\centering}";
2225                                         break;
2226                                 case LYX_ALIGN_NONE:
2227                                 case LYX_ALIGN_BLOCK:
2228                                 case LYX_ALIGN_LAYOUT:
2229                                 case LYX_ALIGN_SPECIAL:
2230                                         break;
2231                                 }
2232
2233                                 switch (column_info[i].valignment) {
2234                                 case LYX_VALIGN_TOP:
2235                                         os << 'p';
2236                                         break;
2237                                 case LYX_VALIGN_MIDDLE:
2238                                         os << 'm';
2239                                         break;
2240                                 case LYX_VALIGN_BOTTOM:
2241                                         os << 'b';
2242                                         break;
2243                         }
2244                                 os << '{'
2245                                    << column_info[i].p_width.asLatexString()
2246                                    << '}';
2247                         } else {
2248                                 switch (column_info[i].alignment) {
2249                                 case LYX_ALIGN_LEFT:
2250                                         os << 'l';
2251                                         break;
2252                                 case LYX_ALIGN_RIGHT:
2253                                         os << 'r';
2254                                         break;
2255                                 default:
2256                                         os << 'c';
2257                                         break;
2258                                 }
2259                         }
2260                         if (!use_booktabs && column_info[i].right_line)
2261                                 os << '|';
2262                 }
2263         }
2264         os << "}\n";
2265         ++ret;
2266
2267         ret += TeXLongtableHeaderFooter(os, buf, runparams);
2268
2269         //+---------------------------------------------------------------------
2270         //+                      the single row and columns (cells)            +
2271         //+---------------------------------------------------------------------
2272
2273         for (row_type i = 0; i < rows_; ++i) {
2274                 if (isValidRow(i)) {
2275                         ret += TeXRow(os, i, buf, runparams);
2276                         if (is_long_tabular && row_info[i].newpage) {
2277                                 os << "\\newpage\n";
2278                                 ++ret;
2279                         }
2280                 }
2281         }
2282
2283         //+---------------------------------------------------------------------
2284         //+                      the closing of the tabular                    +
2285         //+---------------------------------------------------------------------
2286
2287         if (is_long_tabular)
2288                 os << "\\end{longtable}";
2289         else
2290                 os << "\\end{tabular}";
2291         if (rotate) {
2292                 os << "\n\\end{sideways}";
2293                 ++ret;
2294         }
2295
2296         return ret;
2297 }
2298
2299
2300 int LyXTabular::docbookRow(Buffer const & buf, ostream & os, row_type row,
2301                            OutputParams const & runparams) const
2302 {
2303         int ret = 0;
2304         idx_type cell = getFirstCellInRow(row);
2305
2306         os << "<row>\n";
2307         for (col_type j = 0; j < columns_; ++j) {
2308                 if (isPartOfMultiColumn(row, j))
2309                         continue;
2310
2311                 os << "<entry align=\"";
2312                 switch (getAlignment(cell)) {
2313                 case LYX_ALIGN_LEFT:
2314                         os << "left";
2315                         break;
2316                 case LYX_ALIGN_RIGHT:
2317                         os << "right";
2318                         break;
2319                 default:
2320                         os << "center";
2321                         break;
2322                 }
2323
2324                 os << "\" valign=\"";
2325                 switch (getVAlignment(cell)) {
2326                 case LYX_VALIGN_TOP:
2327                         os << "top";
2328                         break;
2329                 case LYX_VALIGN_BOTTOM:
2330                         os << "bottom";
2331                         break;
2332                 case LYX_VALIGN_MIDDLE:
2333                         os << "middle";
2334                 }
2335                 os << '"';
2336
2337                 if (isMultiColumn(cell)) {
2338                         os << " namest=\"col" << j << "\" ";
2339                         os << "nameend=\"col" << j + cells_in_multicolumn(cell) - 1<< '"';
2340                 }
2341
2342                 os << '>';
2343                 ret += getCellInset(cell)->docbook(buf, os, runparams);
2344                 os << "</entry>\n";
2345                 ++cell;
2346         }
2347         os << "</row>\n";
2348         return ret;
2349 }
2350
2351
2352 int LyXTabular::docbook(Buffer const & buf, ostream & os,
2353                         OutputParams const & runparams) const
2354 {
2355         int ret = 0;
2356
2357         //+---------------------------------------------------------------------
2358         //+                      first the opening preamble                    +
2359         //+---------------------------------------------------------------------
2360
2361         os << "<tgroup cols=\"" << columns_
2362            << "\" colsep=\"1\" rowsep=\"1\">\n";
2363
2364         for (col_type i = 0; i < columns_; ++i) {
2365                 os << "<colspec colname=\"col" << i << "\" align=\"";
2366                 switch (column_info[i].alignment) {
2367                 case LYX_ALIGN_LEFT:
2368                         os << "left";
2369                         break;
2370                 case LYX_ALIGN_RIGHT:
2371                         os << "right";
2372                         break;
2373                 default:
2374                         os << "center";
2375                         break;
2376                 }
2377                 os << '"';
2378                 if (runparams.flavor == OutputParams::XML)
2379                         os << '/';
2380                 os << ">\n";
2381                 ++ret;
2382         }
2383
2384         //+---------------------------------------------------------------------
2385         //+                      Long Tabular case                             +
2386         //+---------------------------------------------------------------------
2387
2388         // output header info
2389         if (haveLTHead() || haveLTFirstHead()) {
2390                 os << "<thead>\n";
2391                 ++ret;
2392                 for (row_type i = 0; i < rows_; ++i) {
2393                         if (row_info[i].endhead || row_info[i].endfirsthead) {
2394                                 ret += docbookRow(buf, os, i, runparams);
2395                         }
2396                 }
2397                 os << "</thead>\n";
2398                 ++ret;
2399         }
2400         // output footer info
2401         if (haveLTFoot() || haveLTLastFoot()) {
2402                 os << "<tfoot>\n";
2403                 ++ret;
2404                 for (row_type i = 0; i < rows_; ++i) {
2405                         if (row_info[i].endfoot || row_info[i].endlastfoot) {
2406                                 ret += docbookRow(buf, os, i, runparams);
2407                         }
2408                 }
2409                 os << "</tfoot>\n";
2410                 ++ret;
2411         }
2412
2413         //+---------------------------------------------------------------------
2414         //+                      the single row and columns (cells)            +
2415         //+---------------------------------------------------------------------
2416
2417         os << "<tbody>\n";
2418         ++ret;
2419         for (row_type i = 0; i < rows_; ++i) {
2420                 if (isValidRow(i)) {
2421                         ret += docbookRow(buf, os, i, runparams);
2422                 }
2423         }
2424         os << "</tbody>\n";
2425         ++ret;
2426         //+---------------------------------------------------------------------
2427         //+                      the closing of the tabular                    +
2428         //+---------------------------------------------------------------------
2429
2430         os << "</tgroup>";
2431         ++ret;
2432
2433         return ret;
2434 }
2435
2436
2437 int LyXTabular::asciiTopHLine(ostream & os, row_type row,
2438                               vector<unsigned int> const & clen) const
2439 {
2440         idx_type const fcell = getFirstCellInRow(row);
2441         idx_type const n = numberOfCellsInRow(fcell) + fcell;
2442         idx_type tmp = 0;
2443
2444         for (idx_type i = fcell; i < n; ++i) {
2445                 if (topLine(i)) {
2446                         ++tmp;
2447                         break;
2448                 }
2449         }
2450         if (!tmp)
2451                 return 0;
2452
2453         unsigned char ch;
2454         for (idx_type i = fcell; i < n; ++i) {
2455                 if (topLine(i)) {
2456                         if (leftLine(i))
2457                                 os << "+-";
2458                         else
2459                                 os << "--";
2460                         ch = '-';
2461                 } else {
2462                         os << "  ";
2463                         ch = ' ';
2464                 }
2465                 col_type column = column_of_cell(i);
2466                 int len = clen[column];
2467                 while (column < columns_ - 1
2468                        && isPartOfMultiColumn(row, ++column))
2469                         len += clen[column] + 4;
2470                 os << string(len, ch);
2471                 if (topLine(i)) {
2472                         if (rightLine(i))
2473                                 os << "-+";
2474                         else
2475                                 os << "--";
2476                 } else {
2477                         os << "  ";
2478                 }
2479         }
2480         os << endl;
2481         return 1;
2482 }
2483
2484
2485 int LyXTabular::asciiBottomHLine(ostream & os, row_type row,
2486                                  vector<unsigned int> const & clen) const
2487 {
2488         idx_type const fcell = getFirstCellInRow(row);
2489         idx_type const n = numberOfCellsInRow(fcell) + fcell;
2490         idx_type tmp = 0;
2491
2492         for (idx_type i = fcell; i < n; ++i) {
2493                 if (bottomLine(i)) {
2494                         ++tmp;
2495                         break;
2496                 }
2497         }
2498         if (!tmp)
2499                 return 0;
2500
2501         unsigned char ch;
2502         for (idx_type i = fcell; i < n; ++i) {
2503                 if (bottomLine(i)) {
2504                         if (leftLine(i))
2505                                 os << "+-";
2506                         else
2507                                 os << "--";
2508                         ch = '-';
2509                 } else {
2510                         os << "  ";
2511                         ch = ' ';
2512                 }
2513                 col_type column = column_of_cell(i);
2514                 int len = clen[column];
2515                 while (column < columns_ -1
2516                        && isPartOfMultiColumn(row, ++column))
2517                         len += clen[column] + 4;
2518                 os << string(len, ch);
2519                 if (bottomLine(i)) {
2520                         if (rightLine(i))
2521                                 os << "-+";
2522                         else
2523                                 os << "--";
2524                 } else {
2525                         os << "  ";
2526                 }
2527         }
2528         os << endl;
2529         return 1;
2530 }
2531
2532
2533 int LyXTabular::asciiPrintCell(Buffer const & buf, ostream & os,
2534                                OutputParams const & runparams,
2535                                idx_type cell, row_type row, col_type column,
2536                                vector<unsigned int> const & clen,
2537                                bool onlydata) const
2538 {
2539         ostringstream sstr;
2540         int const ret = getCellInset(cell)->plaintext(buf, sstr, runparams);
2541
2542         if (onlydata) {
2543                 os << sstr.str();
2544                 return ret;
2545         }
2546
2547         if (leftLine(cell))
2548                 os << "| ";
2549         else
2550                 os << "  ";
2551
2552         unsigned int len1 = sstr.str().length();
2553         unsigned int len2 = clen[column];
2554         while (column < columns_ -1
2555                && isPartOfMultiColumn(row, ++column))
2556                 len2 += clen[column] + 4;
2557         len2 -= len1;
2558
2559         switch (getAlignment(cell)) {
2560         default:
2561         case LYX_ALIGN_LEFT:
2562                 len1 = 0;
2563                 break;
2564         case LYX_ALIGN_RIGHT:
2565                 len1 = len2;
2566                 len2 = 0;
2567                 break;
2568         case LYX_ALIGN_CENTER:
2569                 len1 = len2 / 2;
2570                 len2 -= len1;
2571                 break;
2572         }
2573
2574         os << string(len1, ' ') << sstr.str() << string(len2, ' ');
2575
2576         if (rightLine(cell))
2577                 os << " |";
2578         else
2579                 os << "  ";
2580
2581         return ret;
2582 }
2583
2584
2585 int LyXTabular::plaintext(Buffer const & buf, ostream & os,
2586                       OutputParams const & runparams,
2587                       int const depth,
2588                       bool onlydata, unsigned char delim) const
2589 {
2590         int ret = 0;
2591
2592         // first calculate the width of the single columns
2593         vector<unsigned int> clen(columns_);
2594
2595         if (!onlydata) {
2596                 // first all non (real) multicolumn cells!
2597                 for (col_type j = 0; j < columns_; ++j) {
2598                         clen[j] = 0;
2599                         for (row_type i = 0; i < rows_; ++i) {
2600                                 idx_type cell = getCellNumber(i, j);
2601                                 if (isMultiColumnReal(cell))
2602                                         continue;
2603                                 ostringstream sstr;
2604                                 getCellInset(cell)->plaintext(buf, sstr, runparams);
2605                                 if (clen[j] < sstr.str().length())
2606                                         clen[j] = sstr.str().length();
2607                         }
2608                 }
2609                 // then all (real) multicolumn cells!
2610                 for (col_type j = 0; j < columns_; ++j) {
2611                         for (row_type i = 0; i < rows_; ++i) {
2612                                 idx_type cell = getCellNumber(i, j);
2613                                 if (!isMultiColumnReal(cell) || isPartOfMultiColumn(i, j))
2614                                         continue;
2615                                 ostringstream sstr;
2616                                 getCellInset(cell)->plaintext(buf, sstr, runparams);
2617                                 int len = int(sstr.str().length());
2618                                 idx_type const n = cells_in_multicolumn(cell);
2619                                 for (col_type k = j; len > 0 && k < j + n - 1; ++k)
2620                                         len -= clen[k];
2621                                 if (len > int(clen[j + n - 1]))
2622                                         clen[j + n - 1] = len;
2623                         }
2624                 }
2625         }
2626         idx_type cell = 0;
2627         for (row_type i = 0; i < rows_; ++i) {
2628                 if (!onlydata && asciiTopHLine(os, i, clen))
2629                         os << string(depth * 2, ' ');
2630                 for (col_type j = 0; j < columns_; ++j) {
2631                         if (isPartOfMultiColumn(i, j))
2632                                 continue;
2633                         if (onlydata && j > 0)
2634                                 os << delim;
2635                         ret += asciiPrintCell(buf, os, runparams,
2636                                               cell, i, j, clen, onlydata);
2637                         ++cell;
2638                 }
2639                 os << endl;
2640                 if (!onlydata) {
2641                         os << string(depth * 2, ' ');
2642                         if (asciiBottomHLine(os, i, clen))
2643                                 os << string(depth * 2, ' ');
2644                 }
2645         }
2646         return ret;
2647 }
2648
2649
2650 shared_ptr<InsetText> LyXTabular::getCellInset(idx_type cell) const
2651 {
2652         return cell_info[row_of_cell(cell)][column_of_cell(cell)].inset;
2653 }
2654
2655
2656 shared_ptr<InsetText> LyXTabular::getCellInset(row_type row,
2657                                                col_type column) const
2658 {
2659         return cell_info[row][column].inset;
2660 }
2661
2662
2663 void LyXTabular::setCellInset(row_type row, col_type column,
2664                               shared_ptr<InsetText> ins) const
2665 {
2666         cell_info[row][column].inset = ins;
2667 }
2668
2669
2670 LyXTabular::idx_type
2671 LyXTabular::getCellFromInset(InsetBase const * inset) const
2672 {
2673         // is this inset part of the tabular?
2674         if (!inset) {
2675                 lyxerr << "Error: this is not a cell of the tabular!" << endl;
2676                 BOOST_ASSERT(false);
2677         }
2678
2679         for (idx_type cell = 0, n = getNumberOfCells(); cell < n; ++cell)
2680                 if (getCellInset(cell).get() == inset) {
2681                         lyxerr[Debug::INSETTEXT] << "LyXTabular::getCellFromInset: "
2682                                 << "cell=" << cell << endl;
2683                         return cell;
2684                 }
2685
2686         // We should have found a cell at this point
2687         lyxerr << "LyXTabular::getCellFromInset: Cell of inset "
2688                 << inset << " not found!" << endl;
2689         BOOST_ASSERT(false);
2690         // shut up compiler
2691         return 0;
2692 }
2693
2694
2695 void LyXTabular::validate(LaTeXFeatures & features) const
2696 {
2697         features.require("NeedTabularnewline");
2698         if (useBookTabs())
2699                 features.require("booktabs");
2700         if (isLongTabular())
2701                 features.require("longtable");
2702         if (needRotating())
2703                 features.require("rotating");
2704         for (idx_type cell = 0; cell < numberofcells; ++cell) {
2705                 if (getVAlignment(cell) != LYX_VALIGN_TOP ||
2706                      (!getPWidth(cell).zero() && !isMultiColumn(cell)))
2707                         features.require("array");
2708                 getCellInset(cell)->validate(features);
2709         }
2710 }
2711
2712
2713 LyXTabular::BoxType LyXTabular::useParbox(idx_type cell) const
2714 {
2715         ParagraphList const & parlist = getCellInset(cell)->paragraphs();
2716         ParagraphList::const_iterator cit = parlist.begin();
2717         ParagraphList::const_iterator end = parlist.end();
2718
2719         for (; cit != end; ++cit)
2720                 for (int i = 0; i < cit->size(); ++i)
2721                         if (cit->isNewline(i))
2722                                 return BOX_PARBOX;
2723
2724         return BOX_NONE;
2725 }