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