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