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