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