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