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