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