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