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