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