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