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