]> git.lyx.org Git - lyx.git/blob - src/tabular.C
move some selection related stuff over to textcursor.C
[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::setHeaderFooterRows(int hr, int fhr, int fr, int lfr)
1087 {
1088         // set header info
1089         while(hr > 0) {
1090                 row_info[--hr].endhead = true;
1091         }
1092         // set firstheader info
1093         if (fhr && (fhr < rows_)) {
1094                 if (row_info[fhr].endhead) {
1095                         while(fhr > 0) {
1096                                 row_info[--fhr].endfirsthead = true;
1097                                 row_info[fhr].endhead = false;
1098                         }
1099                 } else if (row_info[fhr - 1].endhead) {
1100                         endfirsthead.empty = true;
1101                 } else {
1102                         while((fhr > 0) && !row_info[--fhr].endhead) {
1103                                 row_info[fhr].endfirsthead = true;
1104                         }
1105                 }
1106         }
1107         // set footer info
1108         if (fr && (fr < rows_)) {
1109                 if (row_info[fr].endhead && row_info[fr-1].endhead) {
1110                         while((fr > 0) && !row_info[--fr].endhead) {
1111                                 row_info[fr].endfoot = true;
1112                                 row_info[fr].endhead = false;
1113                         }
1114                 } else if (row_info[fr].endfirsthead && row_info[fr-1].endfirsthead) {
1115                         while((fr > 0) && !row_info[--fr].endfirsthead) {
1116                                 row_info[fr].endfoot = true;
1117                                 row_info[fr].endfirsthead = false;
1118                         }
1119                 } else if (!row_info[fr - 1].endhead && !row_info[fr - 1].endfirsthead) {
1120                         while((fr > 0) && !row_info[--fr].endhead &&
1121                                   !row_info[fr].endfirsthead)
1122                         {
1123                                 row_info[fr].endfoot = true;
1124                         }
1125                 }
1126         }
1127         // set lastfooter info
1128         if (lfr && (lfr < rows_)) {
1129                 if (row_info[lfr].endhead && row_info[lfr - 1].endhead) {
1130                         while((lfr > 0) && !row_info[--lfr].endhead) {
1131                                 row_info[lfr].endlastfoot = true;
1132                                 row_info[lfr].endhead = false;
1133                         }
1134                 } else if (row_info[lfr].endfirsthead &&
1135                                    row_info[lfr - 1].endfirsthead)
1136                 {
1137                         while((lfr > 0) && !row_info[--lfr].endfirsthead) {
1138                                 row_info[lfr].endlastfoot = true;
1139                                 row_info[lfr].endfirsthead = false;
1140                         }
1141                 } else if (row_info[lfr].endfoot
1142                            && row_info[lfr - 1].endfoot) {
1143                         while((lfr > 0) && !row_info[--lfr].endfoot) {
1144                                 row_info[lfr].endlastfoot = true;
1145                                 row_info[lfr].endfoot = false;
1146                         }
1147                 } else if (!row_info[fr - 1].endhead
1148                            && !row_info[fr - 1].endfirsthead &&
1149                                    !row_info[fr - 1].endfoot)
1150                 {
1151                         while((lfr > 0) &&
1152                                   !row_info[--lfr].endhead && !row_info[lfr].endfirsthead &&
1153                                   !row_info[lfr].endfoot)
1154                         {
1155                                 row_info[lfr].endlastfoot = true;
1156                         }
1157                 } else if (haveLTFoot()) {
1158                         endlastfoot.empty = true;
1159                 }
1160         }
1161 }
1162
1163
1164 void LyXTabular::read(Buffer const * buf, LyXLex & lex)
1165 {
1166         string line;
1167         istream & is = lex.getStream();
1168
1169         l_getline(is, line);
1170         if (!prefixIs(line, "<lyxtabular ")
1171                 && !prefixIs(line, "<LyXTabular ")) {
1172                 lyx::Assert(false);
1173                 return;
1174         }
1175
1176         int version;
1177         if (!getTokenValue(line, "version", version))
1178                 return;
1179         lyx::Assert(version >= 2);
1180
1181         int rows_arg;
1182         if (!getTokenValue(line, "rows", rows_arg))
1183                 return;
1184         int columns_arg;
1185         if (!getTokenValue(line, "columns", columns_arg))
1186                 return;
1187         init(buf->params, rows_arg, columns_arg);
1188         l_getline(is, line);
1189         if (!prefixIs(line, "<features")) {
1190                 lyxerr << "Wrong tabular format (expected <features ...> got"
1191                        << line << ')' << endl;
1192                 return;
1193         }
1194         getTokenValue(line, "rotate", rotate);
1195         getTokenValue(line, "islongtable", is_long_tabular);
1196         // compatibility read for old longtable options. Now we can make any
1197         // row part of the header/footer type we want before it was strict
1198         // sequential from the first row down (as LaTeX does it!). So now when
1199         // we find a header/footer line we have to go up the rows and set it
1200         // on all preceding rows till the first or one with already a h/f option
1201         // set. If we find a firstheader on the same line as a header or a
1202         // lastfooter on the same line as a footer then this should be set empty.
1203         // (Jug 20011220)
1204         if (version < 3) {
1205                 int hrow;
1206                 int fhrow;
1207                 int frow;
1208                 int lfrow;
1209
1210                 getTokenValue(line, "endhead", hrow);
1211                 getTokenValue(line, "endfirsthead", fhrow);
1212                 getTokenValue(line, "endfoot", frow);
1213                 getTokenValue(line, "endlastfoot", lfrow);
1214                 setHeaderFooterRows(abs(hrow), abs(fhrow), abs(frow), abs(lfrow));
1215         } else {
1216                 getTokenValue(line, "firstHeadTopDL", endfirsthead.topDL);
1217                 getTokenValue(line, "firstHeadBottomDL", endfirsthead.bottomDL);
1218                 getTokenValue(line, "firstHeadEmpty", endfirsthead.empty);
1219                 getTokenValue(line, "headTopDL", endhead.topDL);
1220                 getTokenValue(line, "headBottomDL", endhead.bottomDL);
1221                 getTokenValue(line, "footTopDL", endfoot.topDL);
1222                 getTokenValue(line, "footBottomDL", endfoot.bottomDL);
1223                 getTokenValue(line, "lastFootTopDL", endlastfoot.topDL);
1224                 getTokenValue(line, "lastFootBottomDL", endlastfoot.bottomDL);
1225                 getTokenValue(line, "lastFootEmpty", endlastfoot.empty);
1226         }
1227         for (int j = 0; j < columns_; ++j) {
1228                 l_getline(is,line);
1229                 if (!prefixIs(line,"<column")) {
1230                         lyxerr << "Wrong tabular format (expected <column ...> got"
1231                                << line << ')' << endl;
1232                         return;
1233                 }
1234                 getTokenValue(line, "alignment", column_info[j].alignment);
1235                 getTokenValue(line, "valignment", column_info[j].valignment);
1236                 getTokenValue(line, "leftline", column_info[j].left_line);
1237                 getTokenValue(line, "rightline", column_info[j].right_line);
1238                 getTokenValue(line, "width", column_info[j].p_width);
1239                 getTokenValue(line, "special", column_info[j].align_special);
1240         }
1241
1242         for (int i = 0; i < rows_; ++i) {
1243                 l_getline(is, line);
1244                 if (!prefixIs(line, "<row")) {
1245                         lyxerr << "Wrong tabular format (expected <row ...> got"
1246                                << line << ')' << endl;
1247                         return;
1248                 }
1249                 getTokenValue(line, "topline", row_info[i].top_line);
1250                 getTokenValue(line, "bottomline", row_info[i].bottom_line);
1251                 getTokenValue(line, "endfirsthead", row_info[i].endfirsthead);
1252                 getTokenValue(line, "endhead", row_info[i].endhead);
1253                 getTokenValue(line, "endfoot", row_info[i].endfoot);
1254                 getTokenValue(line, "endlastfoot", row_info[i].endlastfoot);
1255                 getTokenValue(line, "newpage", row_info[i].newpage);
1256                 for (int j = 0; j < columns_; ++j) {
1257                         l_getline(is, line);
1258                         if (!prefixIs(line, "<cell")) {
1259                                 lyxerr << "Wrong tabular format (expected <cell ...> got"
1260                                        << line << ')' << endl;
1261                                 return;
1262                         }
1263                         getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn);
1264                         getTokenValue(line, "alignment", cell_info[i][j].alignment);
1265                         getTokenValue(line, "valignment", cell_info[i][j].valignment);
1266                         getTokenValue(line, "topline", cell_info[i][j].top_line);
1267                         getTokenValue(line, "bottomline", cell_info[i][j].bottom_line);
1268                         getTokenValue(line, "leftline", cell_info[i][j].left_line);
1269                         getTokenValue(line, "rightline", cell_info[i][j].right_line);
1270                         getTokenValue(line, "rotate", cell_info[i][j].rotate);
1271                         getTokenValue(line, "usebox", cell_info[i][j].usebox);
1272                         getTokenValue(line, "width", cell_info[i][j].p_width);
1273                         getTokenValue(line, "special", cell_info[i][j].align_special);
1274                         l_getline(is, line);
1275                         if (prefixIs(line, "\\begin_inset")) {
1276                                 cell_info[i][j].inset.read(buf, lex);
1277                                 l_getline(is, line);
1278                         }
1279                         if (!prefixIs(line, "</cell>")) {
1280                                 lyxerr << "Wrong tabular format (expected </cell> got"
1281                                        << line << ')' << endl;
1282                                 return;
1283                         }
1284                 }
1285                 l_getline(is, line);
1286                 if (!prefixIs(line, "</row>")) {
1287                         lyxerr << "Wrong tabular format (expected </row> got"
1288                                << line << ')' << endl;
1289                         return;
1290                 }
1291         }
1292         while (!prefixIs(line, "</lyxtabular>")) {
1293                 l_getline(is, line);
1294         }
1295         set_row_column_number_info();
1296 }
1297
1298
1299 bool LyXTabular::isMultiColumn(int cell, bool real) const
1300 {
1301         return (!real || column_of_cell(cell) != right_column_of_cell(cell)) &&
1302                         (cellinfo_of_cell(cell)->multicolumn != LyXTabular::CELL_NORMAL);
1303 }
1304
1305
1306 LyXTabular::cellstruct * LyXTabular::cellinfo_of_cell(int cell) const
1307 {
1308         int const row = row_of_cell(cell);
1309         int const column = column_of_cell(cell);
1310         return &cell_info[row][column];
1311 }
1312
1313
1314 void LyXTabular::setMultiColumn(Buffer * buffer, int cell, int number)
1315 {
1316         cellinfo_of_cell(cell)->multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
1317         cellinfo_of_cell(cell)->alignment = column_info[column_of_cell(cell)].alignment;
1318         cellinfo_of_cell(cell)->top_line = row_info[row_of_cell(cell)].top_line;
1319         cellinfo_of_cell(cell)->bottom_line = row_info[row_of_cell(cell)].bottom_line;
1320         cellinfo_of_cell(cell)->right_line = column_info[column_of_cell(cell+number-1)].right_line;
1321 #if 1
1322         for (int i = 1; i < number; ++i) {
1323                 cellinfo_of_cell(cell + i)->multicolumn = CELL_PART_OF_MULTICOLUMN;
1324                 cellinfo_of_cell(cell)->inset.appendParagraphs(buffer,
1325                         cellinfo_of_cell(cell+i)->inset.paragraphs);
1326                 cellinfo_of_cell(cell + i)->inset.clear(false);
1327         }
1328 #else
1329         for (number--; number > 0; --number) {
1330                 cellinfo_of_cell(cell + number)->multicolumn = CELL_PART_OF_MULTICOLUMN;
1331         }
1332 #endif
1333         set_row_column_number_info();
1334 }
1335
1336
1337 int LyXTabular::cells_in_multicolumn(int cell) const
1338 {
1339         int const row = row_of_cell(cell);
1340         int column = column_of_cell(cell);
1341         int result = 1;
1342         ++column;
1343         while ((column < columns_) &&
1344                    cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN)
1345         {
1346                 ++result;
1347                 ++column;
1348         }
1349         return result;
1350 }
1351
1352
1353 int LyXTabular::unsetMultiColumn(int cell)
1354 {
1355         int const row = row_of_cell(cell);
1356         int column = column_of_cell(cell);
1357
1358         int result = 0;
1359
1360         if (cell_info[row][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) {
1361                 cell_info[row][column].multicolumn = CELL_NORMAL;
1362                 ++column;
1363                 while ((column < columns_) &&
1364                            (cell_info[row][column].multicolumn ==CELL_PART_OF_MULTICOLUMN))
1365                 {
1366                         cell_info[row][column].multicolumn = CELL_NORMAL;
1367                         ++column;
1368                         ++result;
1369                 }
1370         }
1371         set_row_column_number_info();
1372         return result;
1373 }
1374
1375
1376 void LyXTabular::setLongTabular(bool what)
1377 {
1378         is_long_tabular = what;
1379 }
1380
1381
1382 bool LyXTabular::isLongTabular() const
1383 {
1384         return is_long_tabular;
1385 }
1386
1387
1388 void LyXTabular::setRotateTabular(bool flag)
1389 {
1390         rotate = flag;
1391 }
1392
1393
1394 bool LyXTabular::getRotateTabular() const
1395 {
1396         return rotate;
1397 }
1398
1399
1400 void LyXTabular::setRotateCell(int cell, bool flag)
1401 {
1402         cellinfo_of_cell(cell)->rotate = flag;
1403 }
1404
1405
1406 bool LyXTabular::getRotateCell(int cell) const
1407 {
1408         return cellinfo_of_cell(cell)->rotate;
1409 }
1410
1411
1412 bool LyXTabular::needRotating() const
1413 {
1414         if (rotate)
1415                 return true;
1416         for (int i = 0; i < rows_; ++i) {
1417                 for (int j = 0; j < columns_; ++j) {
1418                         if (cell_info[i][j].rotate)
1419                                 return true;
1420                 }
1421         }
1422         return false;
1423 }
1424
1425
1426 bool LyXTabular::isLastCell(int cell) const
1427 {
1428         if (cell + 1 < numberofcells)
1429                 return false;
1430         return true;
1431 }
1432
1433
1434 int LyXTabular::getCellAbove(int cell) const
1435 {
1436         if (row_of_cell(cell) > 0)
1437                 return cell_info[row_of_cell(cell)-1][column_of_cell(cell)].cellno;
1438         return cell;
1439 }
1440
1441
1442 int LyXTabular::getCellBelow(int cell) const
1443 {
1444         if (row_of_cell(cell) + 1 < rows_)
1445                 return cell_info[row_of_cell(cell)+1][column_of_cell(cell)].cellno;
1446         return cell;
1447 }
1448
1449
1450 int LyXTabular::getLastCellAbove(int cell) const
1451 {
1452         if (row_of_cell(cell) <= 0)
1453                 return cell;
1454         if (!isMultiColumn(cell))
1455                 return getCellAbove(cell);
1456         return cell_info[row_of_cell(cell) - 1][right_column_of_cell(cell)].cellno;
1457 }
1458
1459
1460 int LyXTabular::getLastCellBelow(int cell) const
1461 {
1462         if (row_of_cell(cell) + 1 >= rows_)
1463                 return cell;
1464         if (!isMultiColumn(cell))
1465                 return getCellBelow(cell);
1466         return cell_info[row_of_cell(cell) + 1][right_column_of_cell(cell)].cellno;
1467 }
1468
1469
1470 int LyXTabular::getCellNumber(int row, int column) const
1471 {
1472 #if 0
1473         if (column >= columns_)
1474                 column = columns_ - 1;
1475         else if (column < 0)
1476                 column = 0;
1477         if (row >= rows_)
1478                 row = rows_ - 1;
1479         else if (row < 0)
1480                 row = 0;
1481 #else
1482         lyx::Assert(column >= 0 || column < columns_ || row >= 0 || row < rows_);
1483 #endif
1484         return cell_info[row][column].cellno;
1485 }
1486
1487
1488 void LyXTabular::setUsebox(int cell, BoxType type)
1489 {
1490         cellinfo_of_cell(cell)->usebox = type;
1491 }
1492
1493
1494 LyXTabular::BoxType LyXTabular::getUsebox(int cell) const
1495 {
1496         if (column_info[column_of_cell(cell)].p_width.zero() &&
1497                 !(isMultiColumn(cell) && !cellinfo_of_cell(cell)->p_width.zero()))
1498                 return BOX_NONE;
1499         if (cellinfo_of_cell(cell)->usebox > 1)
1500                 return cellinfo_of_cell(cell)->usebox;
1501         return useParbox(cell);
1502 }
1503
1504
1505 ///
1506 //  This are functions used for the longtable support
1507 ///
1508 void LyXTabular::setLTHead(int row, bool flag, ltType const & hd, bool first)
1509 {
1510         if (first) {
1511                 endfirsthead = hd;
1512                 if (hd.set)
1513                         row_info[row].endfirsthead = flag;
1514         } else {
1515                 endhead = hd;
1516                 if (hd.set)
1517                         row_info[row].endhead = flag;
1518         }
1519 }
1520
1521
1522 bool LyXTabular::getRowOfLTHead(int row, ltType & hd) const
1523 {
1524         hd = endhead;
1525         hd.set = haveLTHead();
1526         return row_info[row].endhead;
1527 }
1528
1529
1530 bool LyXTabular::getRowOfLTFirstHead(int row, ltType & hd) const
1531 {
1532         hd = endfirsthead;
1533         hd.set = haveLTFirstHead();
1534         return row_info[row].endfirsthead;
1535 }
1536
1537
1538 void LyXTabular::setLTFoot(int row, bool flag, ltType const & fd, bool last)
1539 {
1540         if (last) {
1541                 endlastfoot = fd;
1542                 if (fd.set)
1543                         row_info[row].endlastfoot = flag;
1544         } else {
1545                 endfoot = fd;
1546                 if (fd.set)
1547                         row_info[row].endfoot = flag;
1548         }
1549 }
1550
1551
1552 bool LyXTabular::getRowOfLTFoot(int row, ltType & fd) const
1553 {
1554         fd = endfoot;
1555         fd.set = haveLTFoot();
1556         return row_info[row].endfoot;
1557 }
1558
1559
1560 bool LyXTabular::getRowOfLTLastFoot(int row, ltType & fd) const
1561 {
1562         fd = endlastfoot;
1563         fd.set = haveLTLastFoot();
1564         return row_info[row].endlastfoot;
1565 }
1566
1567
1568 void LyXTabular::setLTNewPage(int row, bool what)
1569 {
1570         row_info[row].newpage = what;
1571 }
1572
1573
1574 bool LyXTabular::getLTNewPage(int row) const
1575 {
1576         return row_info[row].newpage;
1577 }
1578
1579
1580 bool LyXTabular::haveLTHead() const
1581 {
1582         for (int i = 0; i < rows_; ++i) {
1583                 if (row_info[i].endhead)
1584                         return true;
1585         }
1586         return false;
1587 }
1588
1589
1590 bool LyXTabular::haveLTFirstHead() const
1591 {
1592         if (endfirsthead.empty)
1593                 return false;
1594         for (int i = 0; i < rows_; ++i) {
1595                 if (row_info[i].endfirsthead)
1596                         return true;
1597         }
1598         return false;
1599 }
1600
1601
1602 bool LyXTabular::haveLTFoot() const
1603 {
1604         for (int i = 0; i < rows_; ++i) {
1605                 if (row_info[i].endfoot)
1606                         return true;
1607         }
1608         return false;
1609 }
1610
1611
1612 bool LyXTabular::haveLTLastFoot() const
1613 {
1614         if (endlastfoot.empty)
1615                 return false;
1616         for (int i = 0; i < rows_; ++i) {
1617                 if (row_info[i].endlastfoot)
1618                         return true;
1619         }
1620         return false;
1621 }
1622
1623
1624 // end longtable support functions
1625
1626 bool LyXTabular::setAscentOfRow(int row, int height)
1627 {
1628         if (row >= rows_ || row_info[row].ascent_of_row == height)
1629                 return false;
1630         row_info[row].ascent_of_row = height;
1631         return true;
1632 }
1633
1634
1635 bool LyXTabular::setDescentOfRow(int row, int height)
1636 {
1637         if (row >= rows_ || row_info[row].descent_of_row == height)
1638                 return false;
1639         row_info[row].descent_of_row = height;
1640         return true;
1641 }
1642
1643
1644 int LyXTabular::getAscentOfRow(int row) const
1645 {
1646         if (row >= rows_)
1647                 return 0;
1648         return row_info[row].ascent_of_row;
1649 }
1650
1651
1652 int LyXTabular::getDescentOfRow(int row) const
1653 {
1654         if (row >= rows_)
1655                 return 0;
1656         return row_info[row].descent_of_row;
1657 }
1658
1659
1660 int LyXTabular::getHeightOfTabular() const
1661 {
1662         int height = 0;
1663
1664         for (int row = 0; row < rows_; ++row)
1665                 height += getAscentOfRow(row) + getDescentOfRow(row) +
1666                         getAdditionalHeight(row);
1667         return height;
1668 }
1669
1670
1671 bool LyXTabular::isPartOfMultiColumn(int row, int column) const
1672 {
1673         if ((row >= rows_) || (column >= columns_))
1674                 return false;
1675         return (cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN);
1676 }
1677
1678
1679 int LyXTabular::TeXTopHLine(ostream & os, int row) const
1680 {
1681         if ((row < 0) || (row >= rows_))
1682                 return 0;
1683
1684         int const fcell = getFirstCellInRow(row);
1685         int const n = numberOfCellsInRow(fcell) + fcell;
1686         int tmp = 0;
1687
1688         for (int i = fcell; i < n; ++i) {
1689                 if (topLine(i))
1690                         ++tmp;
1691         }
1692         if (tmp == (n - fcell)) {
1693                 os << "\\hline ";
1694         } else if (tmp) {
1695                 for (int i = fcell; i < n; ++i) {
1696                         if (topLine(i)) {
1697                                 os << "\\cline{"
1698                                    << column_of_cell(i) + 1
1699                                    << '-'
1700                                    << right_column_of_cell(i) + 1
1701                                    << "} ";
1702                         }
1703                 }
1704         } else {
1705                 return 0;
1706         }
1707         os << "\n";
1708         return 1;
1709 }
1710
1711
1712 int LyXTabular::TeXBottomHLine(ostream & os, int row) const
1713 {
1714         if ((row < 0) || (row >= rows_))
1715                 return 0;
1716
1717         int const fcell = getFirstCellInRow(row);
1718         int const n = numberOfCellsInRow(fcell) + fcell;
1719         int tmp = 0;
1720
1721         for (int i = fcell; i < n; ++i) {
1722                 if (bottomLine(i))
1723                         ++tmp;
1724         }
1725         if (tmp == (n - fcell)) {
1726                 os << "\\hline";
1727         } else if (tmp) {
1728                 for (int i = fcell; i < n; ++i) {
1729                         if (bottomLine(i)) {
1730                                 os << "\\cline{"
1731                                    << column_of_cell(i) + 1
1732                                    << '-'
1733                                    << right_column_of_cell(i) + 1
1734                                    << "} ";
1735                         }
1736                 }
1737         } else {
1738                 return 0;
1739         }
1740         os << "\n";
1741         return 1;
1742 }
1743
1744
1745 int LyXTabular::TeXCellPreamble(ostream & os, int cell) const
1746 {
1747         int ret = 0;
1748
1749         if (getRotateCell(cell)) {
1750                 os << "\\begin{sideways}\n";
1751                 ++ret;
1752         }
1753         if (isMultiColumn(cell)) {
1754                 os << "\\multicolumn{" << cells_in_multicolumn(cell) << "}{";
1755                 if (!cellinfo_of_cell(cell)->align_special.empty()) {
1756                         os << cellinfo_of_cell(cell)->align_special << "}{";
1757                 } else {
1758                         if (leftLine(cell) &&
1759                                 (isFirstCellInRow(cell) ||
1760                                  (!isMultiColumn(cell-1) && !leftLine(cell, true) &&
1761                                   !rightLine(cell-1, true))))
1762                         {
1763                                 os << '|';
1764                         }
1765                         if (!getPWidth(cell).zero()) {
1766                                 switch (getVAlignment(cell)) {
1767                                 case LYX_VALIGN_TOP:
1768                                         os << 'p';
1769                                         break;
1770                                 case LYX_VALIGN_CENTER:
1771                                         os << 'm';
1772                                         break;
1773                                 case LYX_VALIGN_BOTTOM:
1774                                         os << 'b';
1775                                         break;
1776                                 }
1777                                 os << '{'
1778                                    << getPWidth(cell).asLatexString()
1779                                    << '}';
1780                         } else {
1781                                 switch (getAlignment(cell)) {
1782                                 case LYX_ALIGN_LEFT:
1783                                         os << 'l';
1784                                         break;
1785                                 case LYX_ALIGN_RIGHT:
1786                                         os << 'r';
1787                                         break;
1788                                 default:
1789                                         os << 'c';
1790                                         break;
1791                                 }
1792                         }
1793                         if (rightLine(cell))
1794                                 os << '|';
1795                         if (((cell + 1) < numberofcells) && !isFirstCellInRow(cell+1) &&
1796                                 leftLine(cell+1))
1797                                 os << '|';
1798                         os << "}{";
1799                 }
1800         }
1801         if (getUsebox(cell) == BOX_PARBOX) {
1802                 os << "\\parbox[";
1803                 switch (getVAlignment(cell)) {
1804                 case LYX_VALIGN_TOP:
1805                         os << 't';
1806                         break;
1807                 case LYX_VALIGN_CENTER:
1808                         os << 'c';
1809                         break;
1810                 case LYX_VALIGN_BOTTOM:
1811                         os << 'b';
1812                         break;
1813                 }
1814                 os << "]{" << getPWidth(cell).asLatexString() << "}{";
1815         } else if (getUsebox(cell) == BOX_MINIPAGE) {
1816                 os << "\\begin{minipage}[";
1817                 switch (getVAlignment(cell)) {
1818                 case LYX_VALIGN_TOP:
1819                         os << 't';
1820                         break;
1821                 case LYX_VALIGN_CENTER:
1822                         os << 'm';
1823                         break;
1824                 case LYX_VALIGN_BOTTOM:
1825                         os << 'b';
1826                         break;
1827                 }
1828                 os << "]{" << getPWidth(cell).asLatexString() << "}\n";
1829                 ++ret;
1830         }
1831         return ret;
1832 }
1833
1834
1835 int LyXTabular::TeXCellPostamble(ostream & os, int cell) const
1836 {
1837         int ret = 0;
1838
1839         // usual cells
1840         if (getUsebox(cell) == BOX_PARBOX)
1841                 os << '}';
1842         else if (getUsebox(cell) == BOX_MINIPAGE) {
1843                 os << "%\n\\end{minipage}";
1844                 ret += 2;
1845         }
1846         if (isMultiColumn(cell)) {
1847                 os << '}';
1848         }
1849         if (getRotateCell(cell)) {
1850                 os << "%\n\\end{sideways}";
1851                 ++ret;
1852         }
1853         return ret;
1854 }
1855
1856
1857 int LyXTabular::TeXLongtableHeaderFooter(ostream & os, Buffer const * buf,
1858                                          LatexRunParams const & runparams) const
1859 {
1860         if (!is_long_tabular)
1861                 return 0;
1862
1863         int ret = 0;
1864         // output header info
1865         if (haveLTHead()) {
1866                 if (endhead.topDL) {
1867                         os << "\\hline\n";
1868                         ++ret;
1869                 }
1870                 for (int i = 0; i < rows_; ++i) {
1871                         if (row_info[i].endhead) {
1872                                 ret += TeXRow(os, i, buf, runparams);
1873                         }
1874                 }
1875                 if (endhead.bottomDL) {
1876                         os << "\\hline\n";
1877                         ++ret;
1878                 }
1879                 os << "\\endhead\n";
1880                 ++ret;
1881                 if (endfirsthead.empty) {
1882                         os << "\\endfirsthead\n";
1883                         ++ret;
1884                 }
1885         }
1886         // output firstheader info
1887         if (haveLTFirstHead()) {
1888                 if (endfirsthead.topDL) {
1889                         os << "\\hline\n";
1890                         ++ret;
1891                 }
1892                 for (int i = 0; i < rows_; ++i) {
1893                         if (row_info[i].endfirsthead) {
1894                                 ret += TeXRow(os, i, buf, runparams);
1895                         }
1896                 }
1897                 if (endfirsthead.bottomDL) {
1898                         os << "\\hline\n";
1899                         ++ret;
1900                 }
1901                 os << "\\endfirsthead\n";
1902                 ++ret;
1903         }
1904         // output footer info
1905         if (haveLTFoot()) {
1906                 if (endfoot.topDL) {
1907                         os << "\\hline\n";
1908                         ++ret;
1909                 }
1910                 for (int i = 0; i < rows_; ++i) {
1911                         if (row_info[i].endfoot) {
1912                                 ret += TeXRow(os, i, buf, runparams);
1913                         }
1914                 }
1915                 if (endfoot.bottomDL) {
1916                         os << "\\hline\n";
1917                         ++ret;
1918                 }
1919                 os << "\\endfoot\n";
1920                 ++ret;
1921                 if (endlastfoot.empty) {
1922                         os << "\\endlastfoot\n";
1923                         ++ret;
1924                 }
1925         }
1926         // output lastfooter info
1927         if (haveLTLastFoot()) {
1928                 if (endlastfoot.topDL) {
1929                         os << "\\hline\n";
1930                         ++ret;
1931                 }
1932                 for (int i = 0; i < rows_; ++i) {
1933                         if (row_info[i].endlastfoot) {
1934                                 ret += TeXRow(os, i, buf, runparams);
1935                         }
1936                 }
1937                 if (endlastfoot.bottomDL) {
1938                         os << "\\hline\n";
1939                         ++ret;
1940                 }
1941                 os << "\\endlastfoot\n";
1942                 ++ret;
1943         }
1944         return ret;
1945 }
1946
1947
1948 bool LyXTabular::isValidRow(int const row) const
1949 {
1950         if (!is_long_tabular)
1951                 return true;
1952         return (!row_info[row].endhead && !row_info[row].endfirsthead &&
1953                         !row_info[row].endfoot && !row_info[row].endlastfoot);
1954 }
1955
1956
1957 int LyXTabular::TeXRow(ostream & os, int const i, Buffer const * buf,
1958                        LatexRunParams const & runparams) const
1959 {
1960         int ret = 0;
1961         int cell = getCellNumber(i, 0);
1962
1963         ret += TeXTopHLine(os, i);
1964         for (int j = 0; j < columns_; ++j) {
1965                 if (isPartOfMultiColumn(i,j))
1966                         continue;
1967                 ret += TeXCellPreamble(os, cell);
1968                 InsetText * inset = getCellInset(cell);
1969
1970                 bool rtl = inset->paragraphs.begin()->isRightToLeftPar(buf->params) &&
1971                         !inset->paragraphs.begin()->empty() && getPWidth(cell).zero();
1972
1973                 if (rtl)
1974                         os << "\\R{";
1975                 ret += inset->latex(buf, os, runparams);
1976                 if (rtl)
1977                         os << '}';
1978
1979                 ret += TeXCellPostamble(os, cell);
1980                 if (!isLastCellInRow(cell)) { // not last cell in row
1981                         os << "&\n";
1982                         ++ret;
1983                 }
1984                 ++cell;
1985         }
1986         os << "\\tabularnewline\n";
1987         ++ret;
1988         ret += TeXBottomHLine(os, i);
1989         return ret;
1990 }
1991
1992
1993 int LyXTabular::latex(Buffer const * buf, ostream & os,
1994                       LatexRunParams const & runparams) const
1995 {
1996         int ret = 0;
1997
1998         //+---------------------------------------------------------------------
1999         //+                      first the opening preamble                    +
2000         //+---------------------------------------------------------------------
2001
2002         if (rotate) {
2003                 os << "\\begin{sideways}\n";
2004                 ++ret;
2005         }
2006         if (is_long_tabular)
2007                 os << "\\begin{longtable}{";
2008         else
2009                 os << "\\begin{tabular}{";
2010         for (int i = 0; i < columns_; ++i) {
2011                 if (!column_info[i].align_special.empty()) {
2012                         os << column_info[i].align_special;
2013                 } else {
2014                         if (column_info[i].left_line)
2015                                 os << '|';
2016                         if (!column_info[i].p_width.zero()) {
2017                                 switch (column_info[i].alignment) {
2018                                 case LYX_ALIGN_LEFT:
2019                                         os << ">{\\raggedright}";
2020                                         break;
2021                                 case LYX_ALIGN_RIGHT:
2022                                         os << ">{\\raggedleft}";
2023                                         break;
2024                                 case LYX_ALIGN_CENTER:
2025                                         os << ">{\\centering}";
2026                                         break;
2027                                 case LYX_ALIGN_NONE:
2028                                 case LYX_ALIGN_BLOCK:
2029                                 case LYX_ALIGN_LAYOUT:
2030                                 case LYX_ALIGN_SPECIAL:
2031                                         break;
2032                                 }
2033
2034                                 switch (column_info[i].valignment) {
2035                                 case LYX_VALIGN_TOP:
2036                                         os << 'p';
2037                                         break;
2038                                 case LYX_VALIGN_CENTER:
2039                                         os << 'm';
2040                                         break;
2041                                 case LYX_VALIGN_BOTTOM:
2042                                         os << 'b';
2043                                         break;
2044                         }
2045                                 os << '{'
2046                                    << column_info[i].p_width.asLatexString()
2047                                    << '}';
2048                         } else {
2049                                 switch (column_info[i].alignment) {
2050                                 case LYX_ALIGN_LEFT:
2051                                         os << 'l';
2052                                         break;
2053                                 case LYX_ALIGN_RIGHT:
2054                                         os << 'r';
2055                                         break;
2056                                 default:
2057                                         os << 'c';
2058                                         break;
2059                                 }
2060                         }
2061                         if (column_info[i].right_line)
2062                                 os << '|';
2063                 }
2064         }
2065         os << "}\n";
2066         ++ret;
2067
2068         ret += TeXLongtableHeaderFooter(os, buf, runparams);
2069
2070         //+---------------------------------------------------------------------
2071         //+                      the single row and columns (cells)            +
2072         //+---------------------------------------------------------------------
2073
2074         for (int i = 0; i < rows_; ++i) {
2075                 if (isValidRow(i)) {
2076                         ret += TeXRow(os, i, buf, runparams);
2077                         if (is_long_tabular && row_info[i].newpage) {
2078                                 os << "\\newpage\n";
2079                                 ++ret;
2080                         }
2081                 }
2082         }
2083
2084         //+---------------------------------------------------------------------
2085         //+                      the closing of the tabular                    +
2086         //+---------------------------------------------------------------------
2087
2088         if (is_long_tabular)
2089                 os << "\\end{longtable}";
2090         else
2091                 os << "\\end{tabular}";
2092         if (rotate) {
2093                 os << "\n\\end{sideways}";
2094                 ++ret;
2095         }
2096
2097         return ret;
2098 }
2099
2100
2101 int LyXTabular::docbookRow(Buffer const * buf, ostream & os, int row) const
2102 {
2103         int ret = 0;
2104         int cell = getFirstCellInRow(row);
2105
2106         os << "<row>\n";
2107         for (int j = 0; j < columns_; ++j) {
2108                 if (isPartOfMultiColumn(row, j))
2109                         continue;
2110
2111                 os << "<entry align=\"";
2112                 switch (getAlignment(cell)) {
2113                 case LYX_ALIGN_LEFT:
2114                         os << "left";
2115                         break;
2116                 case LYX_ALIGN_RIGHT:
2117                         os << "right";
2118                         break;
2119                 default:
2120                         os << "center";
2121                         break;
2122                 }
2123
2124                 os << "\" valign=\"";
2125                 switch (getVAlignment(cell)) {
2126                 case LYX_VALIGN_TOP:
2127                         os << "top";
2128                         break;
2129                 case LYX_VALIGN_BOTTOM:
2130                         os << "bottom";
2131                         break;
2132                 case LYX_VALIGN_CENTER:
2133                         os << "middle";
2134                 }
2135                 os << '"';
2136
2137                 if (isMultiColumn(cell)) {
2138                         os << " namest=\"col" << j << "\" ";
2139                         os << "nameend=\"col" << j + cells_in_multicolumn(cell) - 1<< '"';
2140                 }
2141
2142                 os << '>';
2143                 ret += getCellInset(cell)->docbook(buf, os, true);
2144                 os << "</entry>\n";
2145                 ++cell;
2146         }
2147         os << "</row>\n";
2148         return ret;
2149 }
2150
2151
2152 int LyXTabular::docbook(Buffer const * buf, ostream & os,
2153                         bool /*mixcont*/) const
2154 {
2155         int ret = 0;
2156
2157         //+---------------------------------------------------------------------
2158         //+                      first the opening preamble                    +
2159         //+---------------------------------------------------------------------
2160
2161         os << "<tgroup cols=\"" << columns_
2162            << "\" colsep=\"1\" rowsep=\"1\">\n";
2163
2164         for (int i = 0; i < columns_; ++i) {
2165                 os << "<colspec colname=\"col" << i << "\" align=\"";
2166                 switch (column_info[i].alignment) {
2167                 case LYX_ALIGN_LEFT:
2168                         os << "left";
2169                         break;
2170                 case LYX_ALIGN_RIGHT:
2171                         os << "right";
2172                         break;
2173                 default:
2174                         os << "center";
2175                         break;
2176                 }
2177                 os << "\">\n";
2178                 ++ret;
2179         }
2180
2181         //+---------------------------------------------------------------------
2182         //+                      Long Tabular case                             +
2183         //+---------------------------------------------------------------------
2184
2185         // output header info
2186         if (haveLTHead() || haveLTFirstHead()) {
2187                 os << "<thead>\n";
2188                 ++ret;
2189                 for (int i = 0; i < rows_; ++i) {
2190                         if (row_info[i].endhead || row_info[i].endfirsthead) {
2191                                 ret += docbookRow(buf, os, i);
2192                         }
2193                 }
2194                 os << "</thead>\n";
2195                 ++ret;
2196         }
2197         // output footer info
2198         if (haveLTFoot() || haveLTLastFoot()) {
2199                 os << "<tfoot>\n";
2200                 ++ret;
2201                 for (int i = 0; i < rows_; ++i) {
2202                         if (row_info[i].endfoot || row_info[i].endlastfoot) {
2203                                 ret += docbookRow(buf, os, i);
2204                         }
2205                 }
2206                 os << "</tfoot>\n";
2207                 ++ret;
2208         }
2209
2210         //+---------------------------------------------------------------------
2211         //+                      the single row and columns (cells)            +
2212         //+---------------------------------------------------------------------
2213
2214         os << "<tbody>\n";
2215         ++ret;
2216         for (int i = 0; i < rows_; ++i) {
2217                 if (isValidRow(i)) {
2218                         ret += docbookRow(buf, os, i);
2219                 }
2220         }
2221         os << "</tbody>\n";
2222         ++ret;
2223         //+---------------------------------------------------------------------
2224         //+                      the closing of the tabular                    +
2225         //+---------------------------------------------------------------------
2226
2227         os << "</tgroup>";
2228         ++ret;
2229
2230         return ret;
2231 }
2232
2233
2234 int LyXTabular::asciiTopHLine(ostream & os, int row,
2235                               vector<unsigned int> const & clen) const
2236 {
2237         int const fcell = getFirstCellInRow(row);
2238         int const n = numberOfCellsInRow(fcell) + fcell;
2239         int tmp = 0;
2240
2241         for (int i = fcell; i < n; ++i) {
2242                 if (topLine(i)) {
2243                         ++tmp;
2244                         break;
2245                 }
2246         }
2247         if (!tmp)
2248                 return 0;
2249
2250         unsigned char ch;
2251         for (int i = fcell; i < n; ++i) {
2252                 if (topLine(i)) {
2253                         if (leftLine(i))
2254                                 os << "+-";
2255                         else
2256                                 os << "--";
2257                         ch = '-';
2258                 } else {
2259                         os << "  ";
2260                         ch = ' ';
2261                 }
2262                 int column = column_of_cell(i);
2263                 int len = clen[column];
2264                 while (isPartOfMultiColumn(row, ++column))
2265                         len += clen[column] + 4;
2266                 os << string(len, ch);
2267                 if (topLine(i)) {
2268                         if (rightLine(i))
2269                                 os << "-+";
2270                         else
2271                                 os << "--";
2272                 } else {
2273                         os << "  ";
2274                 }
2275         }
2276         os << endl;
2277         return 1;
2278 }
2279
2280
2281 int LyXTabular::asciiBottomHLine(ostream & os, int row,
2282                                  vector<unsigned int> const & clen) const
2283 {
2284         int const fcell = getFirstCellInRow(row);
2285         int const n = numberOfCellsInRow(fcell) + fcell;
2286         int tmp = 0;
2287
2288         for (int i = fcell; i < n; ++i) {
2289                 if (bottomLine(i)) {
2290                         ++tmp;
2291                         break;
2292                 }
2293         }
2294         if (!tmp)
2295                 return 0;
2296
2297         unsigned char ch;
2298         for (int i = fcell; i < n; ++i) {
2299                 if (bottomLine(i)) {
2300                         if (leftLine(i))
2301                                 os << "+-";
2302                         else
2303                                 os << "--";
2304                         ch = '-';
2305                 } else {
2306                         os << "  ";
2307                         ch = ' ';
2308                 }
2309                 int column = column_of_cell(i);
2310                 int len = clen[column];
2311                 while (isPartOfMultiColumn(row, ++column))
2312                         len += clen[column] + 4;
2313                 os << string(len, ch);
2314                 if (bottomLine(i)) {
2315                         if (rightLine(i))
2316                                 os << "-+";
2317                         else
2318                                 os << "--";
2319                 } else {
2320                         os << "  ";
2321                 }
2322         }
2323         os << endl;
2324         return 1;
2325 }
2326
2327
2328 int LyXTabular::asciiPrintCell(Buffer const * buf, ostream & os,
2329                                int cell, int row, int column,
2330                                vector<unsigned int> const & clen,
2331                                bool onlydata) const
2332 {
2333         ostringstream sstr;
2334         int ret = getCellInset(cell)->ascii(buf, sstr, 0);
2335
2336         if (onlydata) {
2337                 os << sstr.str();
2338                 return ret;
2339         }
2340
2341         if (leftLine(cell))
2342                 os << "| ";
2343         else
2344                 os << "  ";
2345
2346         unsigned int len1 = sstr.str().length();
2347         unsigned int len2 = clen[column];
2348         while (isPartOfMultiColumn(row, ++column))
2349                 len2 += clen[column] + 4;
2350         len2 -= len1;
2351
2352         switch (getAlignment(cell)) {
2353         default:
2354         case LYX_ALIGN_LEFT:
2355                 len1 = 0;
2356                 break;
2357         case LYX_ALIGN_RIGHT:
2358                 len1 = len2;
2359                 len2 = 0;
2360                 break;
2361         case LYX_ALIGN_CENTER:
2362                 len1 = len2 / 2;
2363                 len2 -= len1;
2364                 break;
2365         }
2366
2367         os << string(len1, ' ') << sstr.str() << string(len2, ' ');
2368
2369         if (rightLine(cell))
2370                 os << " |";
2371         else
2372                 os << "  ";
2373
2374         return ret;
2375 }
2376
2377
2378 int LyXTabular::ascii(Buffer const * buf, ostream & os, int const depth,
2379                                           bool onlydata, unsigned char delim) const
2380 {
2381         int ret = 0;
2382
2383         // first calculate the width of the single columns
2384         vector<unsigned int> clen(columns_);
2385
2386         if (!onlydata) {
2387                 // first all non (real) multicolumn cells!
2388                 for (int j = 0; j < columns_; ++j) {
2389                         clen[j] = 0;
2390                         for (int i = 0; i < rows_; ++i) {
2391                                 int cell = getCellNumber(i, j);
2392                                 if (isMultiColumn(cell, true))
2393                                         continue;
2394                                 ostringstream sstr;
2395                                 getCellInset(cell)->ascii(buf, sstr, 0);
2396                                 if (clen[j] < sstr.str().length())
2397                                         clen[j] = sstr.str().length();
2398                         }
2399                 }
2400                 // then all (real) multicolumn cells!
2401                 for (int j = 0; j < columns_; ++j) {
2402                         for (int i = 0; i < rows_; ++i) {
2403                                 int cell = getCellNumber(i, j);
2404                                 if (!isMultiColumn(cell, true) || isPartOfMultiColumn(i, j))
2405                                         continue;
2406                                 ostringstream sstr;
2407                                 getCellInset(cell)->ascii(buf, sstr, 0);
2408                                 int len = int(sstr.str().length());
2409                                 int const n = cells_in_multicolumn(cell);
2410                                 for (int k = j; (len > 0) && (k < (j + n - 1)); ++k)
2411                                         len -= clen[k];
2412                                 if (len > int(clen[j + n - 1]))
2413                                         clen[j + n - 1] = len;
2414                         }
2415                 }
2416         }
2417         int cell = 0;
2418         for (int i = 0; i < rows_; ++i) {
2419                 if (!onlydata) {
2420                         if (asciiTopHLine(os, i, clen)) {
2421                                 for (int j = 0; j < depth; ++j)
2422                                         os << "  ";
2423                         }
2424                 }
2425                 for (int j = 0; j < columns_; ++j) {
2426                         if (isPartOfMultiColumn(i,j))
2427                                 continue;
2428                         if (onlydata && j > 0)
2429                                 os << delim;
2430                         ret += asciiPrintCell(buf, os, cell, i, j, clen, onlydata);
2431                         ++cell;
2432                 }
2433                 os << endl;
2434                 if (!onlydata) {
2435                         for (int j = 0; j < depth; ++j)
2436                                 os << "  ";
2437                         if (asciiBottomHLine(os, i, clen)) {
2438                                 for (int j = 0; j < depth; ++j)
2439                                         os << "  ";
2440                         }
2441                 }
2442         }
2443         return ret;
2444 }
2445
2446
2447 InsetText * LyXTabular::getCellInset(int cell) const
2448 {
2449         cur_cell = cell;
2450         return & cell_info[row_of_cell(cell)][column_of_cell(cell)].inset;
2451 }
2452
2453
2454 InsetText * LyXTabular::getCellInset(int row, int column) const
2455 {
2456         cur_cell = getCellNumber(row, column);
2457         return & cell_info[row][column].inset;
2458 }
2459
2460
2461 int LyXTabular::getCellFromInset(Inset const * inset, int maybe_cell) const
2462 {
2463         // is this inset part of the tabular?
2464         if (!inset || inset->owner() != owner_) {
2465                 lyxerr[Debug::INSETTEXT]
2466                         << "this is not a cell of the tabular!" << endl;
2467                 return -1;
2468         }
2469
2470         const int save_cur_cell = cur_cell;
2471         int cell = cur_cell;
2472         if (getCellInset(cell) != inset) {
2473                 cell = maybe_cell;
2474                 if (cell == -1 || getCellInset(cell) != inset) {
2475                         cell = -1;
2476                 }
2477         }
2478
2479         if (cell == -1) {
2480                 for (cell = getNumberOfCells(); cell >= 0; --cell) {
2481                         if (getCellInset(cell) == inset)
2482                                 break;
2483                 }
2484                 lyxerr[Debug::INSETTEXT]
2485                          << "LyXTabular::getCellFromInset: "
2486                                     << "cell=" << cell
2487                                     << ", cur_cell=" << save_cur_cell
2488                                     << ", maybe_cell=" << maybe_cell
2489                                     << endl;
2490                 // We should have found a cell at this point
2491                 if (cell == -1) {
2492                         lyxerr << "LyXTabular::getCellFromInset: "
2493                                << "Cell not found!" << endl;
2494                 }
2495         }
2496
2497         return cell;
2498 }
2499
2500
2501 void LyXTabular::validate(LaTeXFeatures & features) const
2502 {
2503         features.require("NeedTabularnewline");
2504         if (isLongTabular())
2505                 features.require("longtable");
2506         if (needRotating())
2507                 features.require("rotating");
2508         for (int cell = 0; cell < numberofcells; ++cell) {
2509                 if (getVAlignment(cell) != LYX_VALIGN_TOP ||
2510                      (!getPWidth(cell).zero() && !isMultiColumn(cell)))
2511                         features.require("array");
2512                 getCellInset(cell)->validate(features);
2513         }
2514 }
2515
2516
2517 void LyXTabular::getLabelList(std::vector<string> & list) const
2518 {
2519         for (int i = 0; i < rows_; ++i)
2520                 for (int j = 0; j < columns_; ++j)
2521                         getCellInset(i, j)->getLabelList(list);
2522 }
2523
2524
2525 LyXTabular::BoxType LyXTabular::useParbox(int cell) const
2526 {
2527         ParagraphList const & parlist = getCellInset(cell)->paragraphs;
2528         ParagraphList::const_iterator cit = parlist.begin();
2529         ParagraphList::const_iterator end = parlist.end();
2530
2531         for (; cit != end; ++cit) {
2532                 for (int i = 0; i < cit->size(); ++i) {
2533                         if (cit->isNewline(i))
2534                                 return BOX_PARBOX;
2535                 }
2536         }
2537         return BOX_NONE;
2538 }