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