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