]> git.lyx.org Git - features.git/blob - src/tabular.C
InsetTabular should now be feature-equal to table-support, small fixes.
[features.git] / src / tabular.C
1 /* This file is part of
2  * ====================================================== 
3  * 
4  *           LyX, The Document Processor
5  *       
6  *        Copyright 2000 The LyX Team.
7  *
8  * ====================================================== 
9  */
10
11 #include <config.h>
12
13 #include <algorithm>
14 #include <cstdlib>
15
16 #ifdef __GNUG__
17 #pragma implementation
18 #endif
19
20 #include "tabular.h"
21 #include "debug.h"
22 #include "vspace.h"
23 #include "layout.h"
24 #include "lyx_gui_misc.h"
25 #include "buffer.h"
26 #include "BufferView.h"
27 #include "Painter.h"
28 #include "LaTeXFeatures.h"
29 #include "support/lstrings.h"
30 #include "support/lyxmanip.h"
31 #include "insets/insettabular.h"
32 #include "insets/insettext.h"
33
34 using std::ostream;
35 using std::istream;
36 using std::getline;
37 using std::max;
38 using std::endl;
39 using std::vector;
40
41 static int const WIDTH_OF_LINE = 5;
42
43 /// Define a few methods for the inner structs
44
45 LyXTabular::cellstruct::cellstruct() 
46 {
47     cellno = 0; //should be initilaized correctly later.
48     width_of_cell = 0;
49     multicolumn = LyXTabular::CELL_NORMAL;
50     alignment = LYX_ALIGN_CENTER;
51     top_line = true;
52     bottom_line = false;
53     rotate = false;
54     linebreaks = false;
55     inset = 0;
56 }
57
58
59 LyXTabular::cellstruct::~cellstruct() 
60 {
61     delete inset;
62 }
63
64
65 LyXTabular::cellstruct::cellstruct(cellstruct const & cs)
66 {
67     cellno = cs.cellno;
68     width_of_cell = cs.width_of_cell;
69     multicolumn = cs.multicolumn;
70     alignment = cs.alignment;
71     top_line = cs.top_line;
72     bottom_line = cs.bottom_line;
73     rotate = cs.rotate;
74     linebreaks = cs.linebreaks;
75     inset = 0;
76 //    if (cs.inset)
77 //      inset = static_cast<InsetText *>(cs.inset->Clone());
78 }
79
80
81 LyXTabular::cellstruct & 
82 LyXTabular::cellstruct::operator=(cellstruct const & cs)
83 {
84     cellno = cs.cellno;
85     width_of_cell = cs.width_of_cell;
86     multicolumn = cs.multicolumn;
87     alignment = cs.alignment;
88     top_line = cs.top_line;
89     bottom_line = cs.bottom_line;
90     rotate = cs.rotate;
91     linebreaks = cs.linebreaks;
92     if (cs.inset)
93         inset = static_cast<InsetText *>(cs.inset->Clone());
94     return *this;
95 }
96
97
98 LyXTabular::rowstruct::rowstruct() 
99 {
100     top_line = true;
101     bottom_line = false;
102     ascent_of_row = 0;
103     descent_of_row = 0;
104     newpage = false;
105 }
106
107
108 LyXTabular::columnstruct::columnstruct() 
109 {
110     left_line = true;
111     right_line = false;
112     alignment = LYX_ALIGN_CENTER;
113     width_of_column = 0;
114 }
115
116
117 /* konstruktor */
118 LyXTabular::LyXTabular(InsetTabular * inset, int rows_arg, int columns_arg)
119 {
120     owner_ = inset;
121     Init(rows_arg, columns_arg);
122 }
123
124
125 LyXTabular::LyXTabular(InsetTabular * inset, LyXTabular const & lt)
126 {
127     owner_ = inset;
128     Init(lt.rows_, lt.columns_);
129     
130     operator=(lt);
131 }
132
133
134 LyXTabular::LyXTabular(InsetTabular * inset, LyXLex & lex)
135 {
136     owner_ = inset;
137     Read(lex);
138 }
139
140
141 LyXTabular::~LyXTabular()
142 {
143     delete[] rowofcell;
144     delete[] columnofcell;
145 }
146
147
148 LyXTabular & LyXTabular::operator=(LyXTabular const & lt)
149 {
150     // If this and lt is not of the same size we have a serious bug
151     // So then it is ok to throw an exception, or for now
152     // call abort()
153     Assert(rows_ == lt.rows_ && columns_ == lt.columns_);
154
155     cell_info = lt.cell_info;
156     row_info = lt.row_info;
157     column_info = lt.column_info;
158
159     // long tabular stuff
160     SetLongTabular(lt.is_long_tabular);
161     endhead = lt.endhead;
162     endfoot = lt.endfoot;
163     endfirsthead = lt.endfirsthead;
164     endlastfoot = lt.endlastfoot;
165
166     rotate = lt.rotate;
167
168     Reinit();
169     
170     return *this;
171 }
172
173
174 LyXTabular * LyXTabular::Clone(InsetTabular * inset)
175 {
176     LyXTabular * result = new LyXTabular(inset, *this);
177     ///
178     // don't know if this is good but I need to Clone also
179     // the text-insets here, this is for the Undo-facility!
180     ///
181     int i,j;
182     for(i=0; i < rows_; ++i) {
183         for(j=0; j < columns_; ++j) {
184             delete result->cell_info[i][j].inset;
185             result->cell_info[i][j].inset=new InsetText(*cell_info[i][j].inset,
186                                                         inset->BufferOwner());
187             result->cell_info[i][j].inset->setOwner(inset);
188         }
189     }
190     return result;
191 }
192
193
194 /* activates all lines and sets all widths to 0 */ 
195 void LyXTabular::Init(int rows_arg, int columns_arg)
196 {
197     int i, j;
198     int cellno = 0;
199
200     rows_ = rows_arg;
201     columns_ = columns_arg;
202     row_info = vector<rowstruct>(rows_, rowstruct());
203     column_info = vector<columnstruct>(columns_, columnstruct());
204     cell_info = vector<vector<cellstruct> >
205             (rows_, vector<cellstruct>(columns_, cellstruct()));
206
207     // Jürgen, use iterators.
208     for (i = 0; i < rows_; ++i) {
209         for (j = 0; j < columns_; ++j) {
210             if (!cell_info[i][j].inset)
211                 cell_info[i][j].inset = new InsetText(owner_->BufferOwner());
212             cell_info[i][j].inset->setOwner(owner_);
213             cell_info[i][j].inset->SetDrawLockedFrame(true);
214             cell_info[i][j].cellno = cellno++;
215         }
216     }
217     row_info[i-1].bottom_line = true;
218     row_info[0].bottom_line = true;
219
220     for (i = 0; i < columns_; ++i) {
221         calculate_width_of_column(i);
222     }
223     column_info[columns_-1].right_line = true;
224    
225     calculate_width_of_tabular();
226
227     rowofcell = 0;
228     columnofcell = 0;
229     set_row_column_number_info();
230     is_long_tabular = false;
231     rotate = 0;
232     endhead = -1;
233     endfirsthead = -1;
234     endfoot = -1;
235     endlastfoot = -1;
236 }
237
238
239 void LyXTabular::AppendRow(int cell )
240 {
241 //    cell_vector::iterator cit = cell_info.begin() + row;
242 //    cell_info.insert(cit, vector<cellstruct>(columns_, cellstruct()));
243 //    cell_info.insert(cell_info.begin(), vector<cellstruct>(columns_, cellstruct()));
244
245     ++rows_;
246    
247 //    row_vector r_info = row_vector(rows_, rowstruct());
248
249     cell_vvector c_info = cell_vvector(rows_, cell_vector(columns_,
250                                                           cellstruct()));
251     int row = row_of_cell(cell);
252     int i;
253
254     row_vector::iterator rit = row_info.begin() + row;
255     row_info.insert(rit, rowstruct());
256
257     for(i = 0; i <= row; ++i) {
258 //      r_info[i] = row_info[i];
259         for(int j = 0; j < columns_; ++j) {
260             c_info[i][j] = cell_info[i][j];
261         }
262     }
263     for(i = row+1; i < rows_; ++i) {
264 //      r_info[i] = row_info[i-1];
265         for(int j = 0; j < columns_; ++j) {
266             c_info[i][j] = cell_info[i-1][j];
267         }
268     }
269 //    row_info = r_info;
270     cell_info = c_info;
271     for(i = 0; i < rows_; ++i) {
272         for(int j = 0; j < columns_; ++j) {
273             cell_info[i][j].inset = static_cast<InsetText *>(c_info[i][j].inset->Clone());
274         }
275     }
276     ++row;
277     for (int j = 0; j < columns_; ++j) {
278         cell_info[row][j].inset->clear();
279     }
280     Reinit();
281 }
282
283
284 void LyXTabular::DeleteRow(int row)
285 {
286     if (!(rows_ - 1))
287         return;
288     row_info.erase(row_info.begin() + row); //&row_info[row]);
289     cell_info.erase(cell_info.begin() + row); //&cell_info[row]);
290     --rows_;
291     Reinit();
292 }
293
294
295 void LyXTabular::AppendColumn(int cell)
296 {
297     ++columns_;
298    
299     cell_vvector c_info = cell_vvector(rows_, cell_vector(columns_,
300                                                           cellstruct()));
301     int column = column_of_cell(cell);
302     int i, j;
303     column_vector::iterator cit = column_info.begin() + column;
304     column_info.insert(cit, columnstruct());
305
306     for (i = 0; i < rows_; ++i) {
307         for (j = 0; j <= column; ++j) {
308             c_info[i][j] = cell_info[i][j];
309         }
310         for (j = column+1; j < columns_; ++j) {
311             c_info[i][j] = cell_info[i][j-1];
312         }
313         // care about multicolumns
314         if (cell_info[i][column+1].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) {
315             cell_info[i][column+1].multicolumn = CELL_PART_OF_MULTICOLUMN;
316         }
317         if (((column+1) == columns_) ||
318             (cell_info[i][column+2].multicolumn != CELL_PART_OF_MULTICOLUMN)) {
319             cell_info[i][column+1].multicolumn = LyXTabular::CELL_NORMAL;
320         }
321     }
322     cell_info = c_info;
323     for(i = 0; i < rows_; ++i) {
324         for(j = 0; j < columns_; ++j) {
325             cell_info[i][j].inset = static_cast<InsetText *>(c_info[i][j].inset->Clone());
326         }
327     }
328     ++column;
329     for (i = 0; i < rows_; ++i) {
330         cell_info[i][column].inset->clear();
331     }
332     Reinit();
333 }
334
335
336 void LyXTabular::DeleteColumn(int column)
337 {
338     if (!(columns_ - 1))
339         return;
340     column_info.erase(column_info.begin() + column);
341     for (int i = 0; i < rows_; ++i) {
342         cell_info[i].erase(cell_info[i].begin() + column);
343     }
344     --columns_;
345     Reinit();
346 }
347
348
349 void LyXTabular::Reinit()
350 {   
351     int j;
352
353     int i = 0;
354
355     // Jürgen, use iterators.
356     for (; i < rows_; ++i) {
357         for (j = 0; j < columns_; ++j) {
358             cell_info[i][j].width_of_cell = 0;
359         }
360     }
361   
362     for (i = 0; i < columns_; ++i) {
363         calculate_width_of_column(i);
364     }
365     calculate_width_of_tabular();
366
367     set_row_column_number_info();
368 }
369
370
371 void LyXTabular::set_row_column_number_info()
372 {
373     int c = 0;
374     int column = 0;
375     numberofcells = -1;
376     int row = 0;
377     for (; row < rows_; ++row) {
378         for (column = 0; column<columns_; ++column) {
379             if (cell_info[row][column].multicolumn
380                 != LyXTabular::CELL_PART_OF_MULTICOLUMN)
381                 ++numberofcells;
382             cell_info[row][column].cellno = numberofcells;
383         }
384     }
385     ++numberofcells; // because this is one more than as we start from 0
386     row = 0;
387     column = 0;
388
389     delete [] rowofcell;
390     rowofcell = new int[numberofcells];
391     delete [] columnofcell;
392     columnofcell = new int[numberofcells];
393   
394     while (c < numberofcells && row < rows_ && column < columns_) {
395         rowofcell[c] = row;
396         columnofcell[c] = column;
397         ++c;
398         do {
399             ++column;
400         } while (column < columns_ &&
401                  cell_info[row][column].multicolumn
402                  == LyXTabular::CELL_PART_OF_MULTICOLUMN);
403         if (column == columns_) {
404             column = 0;
405             ++row;
406         }
407     }
408     for (row = 0; row < rows_; ++row) {
409         for (column = 0; column<columns_; ++column) {
410             if (IsPartOfMultiColumn(row,column))
411                 continue;
412             cell_info[row][column].inset->SetAutoBreakRows(
413                 !GetPWidth(GetCellNumber(row, column)).empty());
414         }
415     }
416 }
417
418
419 int LyXTabular::GetNumberOfCells() const
420 {
421     return numberofcells;
422 }
423
424
425 int LyXTabular::NumberOfCellsInRow(int cell) const
426 {
427     int row = row_of_cell(cell);
428     int result = 0;
429     for (int i = 0; i < columns_; ++i) {
430         if (cell_info[row][i].multicolumn != LyXTabular::CELL_PART_OF_MULTICOLUMN)
431             ++result;
432     }
433     return result;
434 }
435
436
437 /* returns 1 if there is a topline, returns 0 if not */ 
438 bool LyXTabular::TopLine(int cell) const
439 {
440     int row = row_of_cell(cell);
441     
442     if (IsMultiColumn(cell))
443         return cellinfo_of_cell(cell)->top_line;
444     return row_info[row].top_line;
445 }
446
447
448 bool LyXTabular::BottomLine(int cell) const
449 {
450     //no bottom line underneath non-existent cells if you please
451     if(cell >= numberofcells)
452         return false;
453
454     if (IsMultiColumn(cell))
455         return cellinfo_of_cell(cell)->bottom_line;
456     return row_info[row_of_cell(cell)].bottom_line;
457 }
458
459
460 bool LyXTabular::LeftLine(int cell) const
461 {
462     return column_info[column_of_cell(cell)].left_line;
463 }
464
465
466 bool LyXTabular::RightLine(int cell) const
467 {
468     return column_info[right_column_of_cell(cell)].right_line;
469 }
470
471
472 bool LyXTabular::TopAlreadyDrawed(int cell) const
473 {
474     if (GetAdditionalHeight(cell))
475         return false;
476     int row = row_of_cell(cell);
477     if (row > 0) {
478         int column = column_of_cell(cell);
479         --row;
480         while (column
481                && cell_info[row][column].multicolumn
482                == LyXTabular::CELL_PART_OF_MULTICOLUMN)
483             --column;
484         if (cell_info[row][column].multicolumn == LyXTabular::CELL_NORMAL)
485             return row_info[row].bottom_line;
486         else
487             return cell_info[row][column].bottom_line;
488     }
489     return false;
490 }
491
492
493 bool LyXTabular::LeftAlreadyDrawed(int cell) const
494 {
495     int column = column_of_cell(cell);
496     if (column > 0) {
497         int row = row_of_cell(cell);
498         while (--column &&
499                (cell_info[row][column].multicolumn ==
500                 LyXTabular::CELL_PART_OF_MULTICOLUMN));
501         if (GetAdditionalWidth(cell_info[row][column].cellno))
502             return false;
503         return column_info[column].right_line;
504     }
505     return false;
506 }
507
508
509 bool LyXTabular::IsLastRow(int cell) const
510 {
511     return (row_of_cell(cell) == rows_ - 1);
512 }
513
514
515 int LyXTabular::GetAdditionalHeight(int cell) const
516 {
517     int row = row_of_cell(cell);
518     if (!row) return 0;
519         
520     int top = 1; // bool top = true; ??
521     int bottom = 1; // bool bottom = true; ??
522     int column;
523
524     for (column = 0; column < columns_ - 1 && bottom; ++column) {
525         switch (cell_info[row - 1][column].multicolumn) {
526         case LyXTabular::CELL_BEGIN_OF_MULTICOLUMN:
527             bottom = cell_info[row - 1][column].bottom_line;
528             break;
529         case LyXTabular::CELL_NORMAL:
530             bottom = row_info[row - 1].bottom_line;
531         }
532     }
533     for (column = 0; column < columns_ - 1 && top; ++column) {
534         switch (cell_info[row][column].multicolumn){
535         case LyXTabular::CELL_BEGIN_OF_MULTICOLUMN:
536             top = cell_info[row][column].top_line;
537             break;
538         case LyXTabular::CELL_NORMAL:
539             top = row_info[row].top_line;
540         }
541     }
542     if (top && bottom)
543         return WIDTH_OF_LINE;
544     return 0;
545 }
546
547
548 int LyXTabular::GetAdditionalWidth(int cell) const
549 {
550     // internally already set in SetWidthOfCell
551     // used to get it back in text.C
552     int col = right_column_of_cell(cell);
553     if (col < columns_ - 1 && column_info[col].right_line &&
554         column_info[col+1].left_line)
555         return WIDTH_OF_LINE;
556     else
557         return 0;
558 }
559
560
561 // returns the maximum over all rows 
562 int LyXTabular::GetWidthOfColumn(int cell) const
563 {
564     int column1 = column_of_cell(cell);
565     int column2 = right_column_of_cell(cell);
566     int result = 0;
567     int i = column1;
568     for (; i <= column2; ++i) {
569         result += column_info[i].width_of_column;
570     }
571     return result;
572 }
573
574
575 int LyXTabular::GetWidthOfTabular() const
576 {
577     return width_of_tabular;
578 }
579
580
581 /* returns 1 if a complete update is necessary, otherwise 0 */ 
582 bool LyXTabular::SetWidthOfMulticolCell(int cell, int new_width)
583 {
584     if (!IsMultiColumn(cell))
585         return false;
586     
587     int row = row_of_cell(cell);
588     int column1 = column_of_cell(cell);
589     int column2 = right_column_of_cell(cell);
590
591     // first set columns to 0 so we can calculate the right width
592     int i = column1;
593     for (; i <= column2; ++i) {
594         cell_info[row][i].width_of_cell = 0;
595     }
596     // set the width to MAX_WIDTH until width > 0
597     int width = (new_width + 2 * WIDTH_OF_LINE);
598     for (i = column1; (i < column2) && (width > 0); ++i) {
599         cell_info[row][i].width_of_cell = column_info[i].width_of_column;
600         width -= column_info[i].width_of_column;
601     }
602     if (i == column2) {
603         cell_info[row][i].width_of_cell = width;
604     }
605     return true;
606 }
607
608
609 void LyXTabular::recalculateMulticolCells(int cell, int new_width)
610 {
611     int row = row_of_cell(cell);
612     int column1 = column_of_cell(cell);
613     int column2 = right_column_of_cell(cell);
614
615     // first set columns to 0 so we can calculate the right width
616     int i = column1;
617     for (; i <= column2; ++i)
618         cell_info[row][i].width_of_cell = 0;
619     for(i = cell + 1; (i < numberofcells) && (!IsMultiColumn(i)); ++i)
620         ;
621     if (i < numberofcells)
622         recalculateMulticolCells(i, GetWidthOfCell(i) - (2 * WIDTH_OF_LINE));
623     SetWidthOfMulticolCell(cell, new_width);
624 }
625
626
627 /* returns 1 if a complete update is necessary, otherwise 0 */ 
628 bool LyXTabular::SetWidthOfCell(int cell, int new_width)
629 {
630     int row = row_of_cell(cell);
631     int column1 = column_of_cell(cell);
632     int tmp = 0;
633     int width = 0;
634
635     if (IsMultiColumn(cell)) {
636         tmp = SetWidthOfMulticolCell(cell, new_width);
637     } else {
638         width = (new_width + 2*WIDTH_OF_LINE);
639         cell_info[row][column1].width_of_cell = width;
640         if (column_info[column1].right_line && (column1 < columns_-1) &&
641             column_info[column1+1].left_line) // additional width
642             cell_info[row][column1].width_of_cell += WIDTH_OF_LINE;
643         tmp = calculate_width_of_column_NMC(column1);
644     }
645     if (tmp) {
646         int i;
647         for(i = 0; i<columns_;++i)
648             calculate_width_of_column_NMC(i);
649         for(i = 0; (i<numberofcells) && !IsMultiColumn(i); ++i)
650             ;
651         if (i<numberofcells)
652             recalculateMulticolCells(i, GetWidthOfCell(i)-(2*WIDTH_OF_LINE));
653         for(i = 0; i<columns_;++i)
654             calculate_width_of_column(i);
655         calculate_width_of_tabular();
656         return true;
657     }
658     return false;
659 }
660
661
662 bool LyXTabular::SetAlignment(int cell, char align)
663 {
664     if (!IsMultiColumn(cell))
665         column_info[column_of_cell(cell)].alignment = align;
666     cellinfo_of_cell(cell)->alignment = align;
667     return true;
668 }
669
670
671 bool LyXTabular::SetPWidth(int cell, string const & width)
672 {
673     bool flag = !width.empty();
674
675     if (IsMultiColumn(cell)) {
676         cellinfo_of_cell(cell)->p_width = width;
677         GetCellInset(cell)->SetAutoBreakRows(flag);
678     } else {
679         int j = column_of_cell(cell);
680         int c;
681         column_info[j].p_width = width;
682         if (flag) // do this only if there is a width
683                 SetAlignment(cell, LYX_ALIGN_LEFT);
684         for(int i=0; i < rows_; ++i) {
685             c = GetCellNumber(i, j);
686             flag = !GetPWidth(c).empty(); // because of multicolumns!
687             GetCellInset(c)->SetAutoBreakRows(flag);
688         }
689     }
690     return true;
691 }
692
693
694 bool LyXTabular::SetAlignSpecial(int cell, string const & special, int what)
695 {
696     if (what == SET_SPECIAL_MULTI)
697         cellinfo_of_cell(cell)->align_special = special;
698     else
699         column_info[column_of_cell(cell)].align_special = special;
700     return true;
701 }
702
703
704 bool LyXTabular::SetAllLines(int cell, bool line)
705 {
706     SetTopLine(cell, line);
707     SetBottomLine(cell, line);
708     SetRightLine(cell, line);
709     SetLeftLine(cell, line);
710     return true;
711 }
712
713
714 bool LyXTabular::SetTopLine(int cell, bool line)
715 {
716     int row = row_of_cell(cell);
717
718     if (!IsMultiColumn(cell))
719         row_info[row].top_line = line;
720     else
721         cellinfo_of_cell(cell)->top_line = line;
722     return true;
723 }
724
725
726 bool LyXTabular::SetBottomLine(int cell, bool line)
727 {
728     if (!IsMultiColumn(cell))
729         row_info[row_of_cell(cell)].bottom_line = line;
730     else
731         cellinfo_of_cell(cell)->bottom_line = line;
732     return true;
733 }
734
735
736 bool LyXTabular::SetLeftLine(int cell, bool line)
737 {
738     column_info[column_of_cell(cell)].left_line = line;
739     return true;
740 }
741
742
743 bool LyXTabular::SetRightLine(int cell, bool line)
744 {
745     column_info[right_column_of_cell(cell)].right_line = line;
746     return true;
747 }
748
749
750 char LyXTabular::GetAlignment(int cell) const
751 {
752     if (IsMultiColumn(cell))
753         return cellinfo_of_cell(cell)->alignment;
754     else
755         return column_info[column_of_cell(cell)].alignment;
756 }
757
758
759 string LyXTabular::GetPWidth(int cell) const
760 {
761     if (IsMultiColumn(cell))
762         return cellinfo_of_cell(cell)->p_width;
763     return column_info[column_of_cell(cell)].p_width;
764 }
765
766
767 string LyXTabular::GetAlignSpecial(int cell, int what) const
768 {
769     if (what == SET_SPECIAL_MULTI)
770         return cellinfo_of_cell(cell)->align_special;
771     return column_info[column_of_cell(cell)].align_special;
772 }
773
774
775 int LyXTabular::GetWidthOfCell(int cell) const
776 {
777     int row = row_of_cell(cell);
778     int column1 = column_of_cell(cell);
779     int column2 = right_column_of_cell(cell);
780     int result = 0;
781     int i = column1;
782     for (; i <= column2; ++i) {
783         result += cell_info[row][i].width_of_cell;
784     }
785     
786 //    result += GetAdditionalWidth(cell);
787     
788     return result;
789 }
790
791 int LyXTabular::GetBeginningOfTextInCell(int cell) const
792 {
793     int x = 0;
794    
795     switch (GetAlignment(cell)){
796     case LYX_ALIGN_CENTER:
797         x += (GetWidthOfColumn(cell) - GetWidthOfCell(cell)) / 2;
798         break;
799     case LYX_ALIGN_RIGHT:
800         x += GetWidthOfColumn(cell) - GetWidthOfCell(cell);
801         // + GetAdditionalWidth(cell);
802         break;
803     default: /* LYX_ALIGN_LEFT: nothing :-) */ 
804         break;
805     }
806     
807     // the LaTeX Way :-(
808     x += WIDTH_OF_LINE;
809     return x;
810 }
811
812
813 bool LyXTabular::IsFirstCellInRow(int cell) const
814 {
815     return (column_of_cell(cell) == 0);
816 }
817
818
819 int LyXTabular::GetFirstCellInRow(int row) const
820 {
821     if (row > (rows_-1))
822         row = rows_ - 1;
823     return cell_info[row][0].cellno;
824 }
825
826 bool LyXTabular::IsLastCellInRow(int cell) const
827 {
828     return (right_column_of_cell(cell) == (columns_ - 1));
829 }
830
831
832 int LyXTabular::GetLastCellInRow(int row) const
833 {
834     if (row > (rows_-1))
835         row = rows_ - 1;
836     return cell_info[row][columns_-1].cellno;
837 }
838
839
840 bool LyXTabular::calculate_width_of_column(int column)
841 {
842     int old_column_width = column_info[column].width_of_column;
843     int maximum = 0;
844     
845     for (int i = 0; i < rows_; ++i) {
846         maximum = max(cell_info[i][column].width_of_cell, maximum);
847     }
848     column_info[column].width_of_column = maximum;
849     return (column_info[column].width_of_column != old_column_width);
850 }
851
852
853 bool LyXTabular::calculate_width_of_column_NMC(int column)
854 {
855     int old_column_width = column_info[column].width_of_column;
856     int max = 0;
857     for (int i = 0; i < rows_; ++i) {
858         if (!IsMultiColumn(GetCellNumber(i, column)) &&
859             (cell_info[i][column].width_of_cell > max)) {
860             max = cell_info[i][column].width_of_cell;
861         }
862     }
863     column_info[column].width_of_column = max;
864     return (column_info[column].width_of_column != old_column_width);
865 }
866
867
868 void LyXTabular::calculate_width_of_tabular()
869 {
870     width_of_tabular = 0;
871     for (int i = 0; i < columns_; ++i) {
872         width_of_tabular += column_info[i].width_of_column;
873     }
874 }
875
876
877 int LyXTabular::row_of_cell(int cell) const
878 {
879     if (cell >= numberofcells)
880         return rows_-1;
881     else if (cell < 0)
882         return 0;
883     return rowofcell[cell];
884 }
885
886
887 int LyXTabular::column_of_cell(int cell) const
888 {
889     if (cell >= numberofcells)
890         return columns_-1;
891     else if (cell < 0)
892         return 0;
893     return columnofcell[cell];
894 }
895
896
897 int LyXTabular::right_column_of_cell(int cell) const
898 {
899     int row = row_of_cell(cell);
900     int column = column_of_cell(cell);
901     while (column < (columns_ - 1) &&
902            cell_info[row][column+1].multicolumn == LyXTabular::CELL_PART_OF_MULTICOLUMN)
903         ++column;
904     return column;
905 }
906
907
908 void LyXTabular::Write(ostream & os) const
909 {
910     int i, j;
911
912     // header line
913     os << "<LyXTabular version=1 rows=" << rows_ << " columns=" << columns_ <<
914         ">" << endl;
915     // global longtable options
916     os << "<Features rotate=" << rotate <<
917         " islongtable=" << is_long_tabular <<
918         " endhead=" << endhead << " endfirsthead=" << endfirsthead <<
919         " endfoot=" << endfoot << " endlastfoot=" << endlastfoot <<
920         ">" << endl << endl;
921     for (i = 0; i < rows_; ++i) {
922         os << "<Row topline=" << row_info[i].top_line <<
923             " bottomline=" << row_info[i].bottom_line <<
924             " newpage=" << row_info[i].newpage <<
925             ">" << endl;
926         for (j = 0; j < columns_; ++j) {
927             if (!i) {
928                 os << "<Column alignment=" << column_info[j].alignment <<
929                     " leftline=" << column_info[j].left_line <<
930                     " rightline=" << column_info[j].right_line <<
931                     " width=\"" << VSpace(column_info[j].p_width).asLyXCommand() <<
932                     "\" special=\"" << column_info[j].align_special <<
933                     "\">" << endl;
934             } else {
935                 os << "<Column>" << endl;
936             }
937             os << "<Cell multicolumn=" << cell_info[i][j].multicolumn <<
938                 " alignment=" << cell_info[i][j].alignment <<
939                 " topline=" << cell_info[i][j].top_line <<
940                 " bottomline=" << cell_info[i][j].bottom_line <<
941                 " rotate=" << cell_info[i][j].rotate <<
942                 " linebreaks=" << cell_info[i][j].linebreaks <<
943                 " width=\"" << cell_info[i][j].p_width <<
944                 "\" special=\"" << cell_info[i][j].align_special <<
945                 "\">" << endl;
946             os << "\\begin_inset ";
947             cell_info[i][j].inset->Write(os);
948             os << "\n\\end_inset " << endl;
949             os << "</Cell>" << endl;
950             os << "</Column>" << endl;
951         }
952         os << "</Row>" << endl;
953     }
954     os << "</LyXTabular>" << endl;
955 }
956
957
958 static
959 bool getTokenValue(string const str, const char * token, string & ret)
960 {
961     int pos = str.find(token);
962     char ch = str[pos+strlen(token)];
963
964     if ((pos < 0) || (ch != '='))
965         return false;
966     ret.erase();
967     pos += strlen(token)+1;
968     ch = str[pos];
969     if ((ch != '"') && (ch != '\'')) { // only read till next space
970         ret += ch;
971         ch = ' ';
972     }
973     while((pos < int(str.length()-1)) && (str[++pos] != ch))
974         ret += str[pos];
975
976     return true;
977 }
978
979
980 static
981 bool getTokenValue(string const str, const char * token, int & num)
982 {
983     string ret;
984     int pos = str.find(token);
985     char ch = str[pos+strlen(token)];
986
987     if ((pos < 0) || (ch != '='))
988         return false;
989     ret.erase();
990     pos += strlen(token)+1;
991     ch = str[pos];
992     if ((ch != '"') && (ch != '\'')) { // only read till next space
993         if (!isdigit(ch))
994             return false;
995         ret += ch;
996     }
997     ++pos;
998     while((pos < int(str.length()-1)) && isdigit(str[pos]))
999         ret += str[pos++];
1000
1001     num = strToInt(ret);
1002     return true;
1003 }
1004
1005
1006 static
1007 bool getTokenValue(string const str, const char * token, bool & flag)
1008 {
1009     string ret;
1010     int pos = str.find(token);
1011     char ch = str[pos+strlen(token)];
1012
1013     if ((pos < 0) || (ch != '='))
1014         return false;
1015     ret.erase();
1016     pos += strlen(token)+1;
1017     ch = str[pos];
1018     if ((ch != '"') && (ch != '\'')) { // only read till next space
1019         if (!isdigit(ch))
1020             return false;
1021         ret += ch;
1022     }
1023     ++pos;
1024     while((pos < int(str.length()-1)) && isdigit(str[pos]))
1025         ret += str[pos++];
1026
1027     flag = strToInt(ret);
1028     return true;
1029 }
1030
1031
1032 void l_getline(istream & is, string & str)
1033 {
1034     getline(is, str);
1035     while(str.empty())
1036         getline(is, str);
1037 }
1038
1039
1040 void LyXTabular::Read(LyXLex & lex)
1041 {
1042     string line;
1043     istream & is = lex.getStream();
1044
1045     l_getline(is, line);
1046     if (!prefixIs(line, "<LyXTabular ")) {
1047         OldFormatRead(lex, line);
1048         return;
1049     }
1050
1051     int version;
1052     int rows_arg;
1053     int columns_arg;
1054     if (!getTokenValue(line, "version", version))
1055         return;
1056     if (!getTokenValue(line, "rows", rows_arg))
1057         return;
1058     if (!getTokenValue(line, "columns", columns_arg))
1059         return;
1060     Init(rows_arg, columns_arg);
1061     l_getline(is, line);
1062     if (!prefixIs(line, "<Features ")) {
1063         lyxerr << "Wrong tabular format (expected <Feture ...> got" <<
1064             line << ")" << endl;
1065         return;
1066     }
1067     (void)getTokenValue(line, "islongtable", is_long_tabular);
1068     (void)getTokenValue(line, "endhead", endhead);
1069     (void)getTokenValue(line, "endfirsthead", endfirsthead);
1070     (void)getTokenValue(line, "endfoot", endfoot);
1071     (void)getTokenValue(line, "endlastfoot", endlastfoot);
1072     int i, j;
1073     for(i = 0; i < rows_; ++i) {
1074         l_getline(is, line);
1075         if (!prefixIs(line, "<Row ")) {
1076             lyxerr << "Wrong tabular format (expected <Row ...> got" <<
1077                 line << ")" << endl;
1078             return;
1079         }
1080         (void)getTokenValue(line, "topline", row_info[i].top_line);
1081         (void)getTokenValue(line, "bottomline", row_info[i].bottom_line);
1082         (void)getTokenValue(line, "newpage", row_info[i].newpage);
1083         for (j = 0; j < columns_; ++j) {
1084             l_getline(is,line);
1085             if (!prefixIs(line,"<Column")) {
1086                 lyxerr << "Wrong tabular format (expected <Column ...> got" <<
1087                     line << ")" << endl;
1088                 return;
1089             }
1090             if (!i) {
1091                 (void)getTokenValue(line, "alignment", column_info[j].alignment);
1092                 (void)getTokenValue(line, "leftline", column_info[j].left_line);
1093                 (void)getTokenValue(line, "rightline", column_info[j].right_line);
1094                 (void)getTokenValue(line, "width", column_info[j].p_width);
1095                 (void)getTokenValue(line, "special", column_info[j].align_special);
1096             }
1097             l_getline(is, line);
1098             if (!prefixIs(line, "<Cell")) {
1099                 lyxerr << "Wrong tabular format (expected <Cell ...> got" <<
1100                     line << ")" << endl;
1101                 return;
1102             }
1103             (void)getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn);
1104             (void)getTokenValue(line, "alignment", cell_info[i][j].alignment);
1105             (void)getTokenValue(line, "topline", cell_info[i][j].top_line);
1106             (void)getTokenValue(line, "bottomline", cell_info[i][j].bottom_line);
1107             (void)getTokenValue(line, "rotate", cell_info[i][j].rotate);
1108             (void)getTokenValue(line, "linebreaks", cell_info[i][j].linebreaks);
1109             (void)getTokenValue(line, "width", cell_info[i][j].p_width);
1110             (void)getTokenValue(line, "special", cell_info[i][j].align_special);
1111             l_getline(is, line);
1112             if (prefixIs(line, "\\begin_inset")) {
1113                 cell_info[i][j].inset->Read(lex);
1114                 l_getline(is, line);
1115             }
1116             if (line != "</Cell>") {
1117                 lyxerr << "Wrong tabular format (expected </Cell> got" <<
1118                     line << ")" << endl;
1119                 return;
1120             }
1121             l_getline(is, line);
1122             if (line != "</Column>") {
1123                 lyxerr << "Wrong tabular format (expected </Column> got" <<
1124                     line << ")" << endl;
1125                 return;
1126             }
1127         }
1128         l_getline(is, line);
1129         if (line != "</Row>") {
1130             lyxerr << "Wrong tabular format (expected </Row> got" <<
1131                 line << ")" << endl;
1132             return;
1133         }
1134     }
1135     while (line != "</LyXTabular>") {
1136         l_getline(is, line);
1137     }
1138     set_row_column_number_info();
1139 }
1140
1141
1142 void LyXTabular::OldFormatRead(LyXLex & lex, string const & fl)
1143 {
1144     int version;
1145     int i, j;
1146     int rows_arg = 0;
1147     int columns_arg = 0;
1148     int is_long_tabular_arg = false;
1149     int rotate_arg = false;
1150     int a = -1;
1151     int b = -1;
1152     int c = -1;
1153     int d = -1;
1154     int e = 0;
1155     int f = 0;
1156     int g = 0;
1157     int h = 0;
1158         
1159     istream & is = lex.getStream();
1160     string s(fl);
1161     if (s.length() > 8)
1162         version = atoi(s.c_str() + 8);
1163     else
1164         version = 1;
1165
1166     vector<int> cont_row_info;
1167
1168     if (version < 5) {
1169         lyxerr << "Tabular format < 5 is not supported anymore\n"
1170             "Get an older version of LyX (< 1.1.x) for conversion!"
1171                << endl;
1172         WriteAlert(_("Warning:"),
1173                    _("Tabular format < 5 is not supported anymore\n"),
1174                    _("Get an older version of LyX (< 1.1.x) for conversion!"));
1175         if (version > 2) {
1176             is >> rows_arg >> columns_arg >> is_long_tabular_arg
1177                >> rotate_arg >> a >> b >> c >> d;
1178         } else
1179             is >> rows_arg >> columns_arg;
1180         Init(rows_arg, columns_arg);
1181         cont_row_info = vector<int>(rows_arg);
1182         SetLongTabular(is_long_tabular_arg);
1183         SetRotateTabular(rotate_arg);
1184         string tmp;
1185         for (i = 0; i < rows_; ++i) {
1186             getline(is, tmp);
1187             cont_row_info[i] = false;
1188         }
1189         for (i = 0; i < columns_; ++i) {
1190             getline(is, tmp);
1191         }
1192         for (i = 0; i < rows_; ++i) {
1193             for (j = 0; j < columns_; ++j) {
1194                 getline(is, tmp);
1195             }
1196         }
1197     } else {
1198         is >> rows_arg >> columns_arg >> is_long_tabular_arg
1199            >> rotate_arg >> a >> b >> c >> d;
1200         Init(rows_arg, columns_arg);
1201         cont_row_info = vector<int>(rows_arg);
1202         SetLongTabular(is_long_tabular_arg);
1203         SetRotateTabular(rotate_arg);
1204         endhead = a;
1205         endfirsthead = b;
1206         endfoot = c;
1207         endlastfoot = d;
1208         for (i = 0; i < rows_; ++i) {
1209             a = b = c = d = e = f = g = h = 0;
1210             is >> a >> b >> c >> d;
1211             row_info[i].top_line = a;
1212             row_info[i].bottom_line = b;
1213             cont_row_info[i] = c;
1214             row_info[i].newpage = d;
1215         }
1216         for (i = 0; i < columns_; ++i) {
1217             string s1;
1218             string s2;
1219             is >> a >> b >> c;
1220             char ch; // skip '"'
1221             is >> ch;
1222             getline(is, s1, '"');
1223             is >> ch; // skip '"'
1224             getline(is, s2, '"');
1225             column_info[i].alignment = static_cast<char>(a);
1226             column_info[i].left_line = b;
1227             column_info[i].right_line = c;
1228             column_info[i].p_width = s1;
1229             column_info[i].align_special = s2;
1230         }
1231         for (i = 0; i < rows_; ++i) {
1232             for (j = 0; j < columns_; ++j) {
1233                 string s1;
1234                 string s2;
1235                 is >> a >> b >> c >> d >> e >> f >> g;
1236                 char ch;
1237                 is >> ch; // skip '"'
1238                 getline(is, s1, '"');
1239                 is >> ch; // skip '"'
1240                 getline(is, s2, '"');
1241                 cell_info[i][j].multicolumn = static_cast<char>(a);
1242                 cell_info[i][j].alignment = static_cast<char>(b);
1243                 cell_info[i][j].top_line = static_cast<char>(c);
1244                 cell_info[i][j].bottom_line = static_cast<char>(d);
1245                 cell_info[i][j].rotate = static_cast<bool>(f);
1246                 cell_info[i][j].linebreaks = static_cast<bool>(g);
1247                 cell_info[i][j].align_special = s1;
1248                 cell_info[i][j].p_width = s2;
1249             }
1250         }
1251     }
1252     set_row_column_number_info();
1253
1254     LyXParagraph * par = new LyXParagraph;
1255     LyXParagraph * return_par = 0;
1256     LyXParagraph::footnote_flag footnoteflag = LyXParagraph::NO_FOOTNOTE;
1257     LyXParagraph::footnote_kind footnotekind = LyXParagraph::FOOTNOTE;
1258     string token, tmptok;
1259     int pos = 0;
1260     char depth = 0;
1261     LyXFont font(LyXFont::ALL_SANE);
1262
1263     while (lex.IsOK()) {
1264         lex.nextToken();
1265         token = lex.GetString();
1266         if (token.empty())
1267             continue;
1268         if ((token == "\\layout") || (token == "\\end_float") ||
1269             (token == "\\end_deeper"))
1270         {
1271             lex.pushToken(token);
1272             break;
1273         }
1274         if (owner_->BufferOwner()->parseSingleLyXformat2Token(lex, par,
1275                                                               return_par,
1276                                                               token, pos,
1277                                                               depth, font,
1278                                                               footnoteflag,
1279                                                               footnotekind))
1280         {
1281             // the_end read
1282             lex.pushToken(token);
1283             break;
1284         }
1285         if (return_par) {
1286             lex.printError("New Paragraph allocated! This should not happen!");
1287             lex.pushToken(token);
1288             delete par;
1289             par = return_par;
1290             break;
1291         }
1292     }
1293     // now we have the par we should fill the insets with this!
1294     int cell = 0;
1295     InsetText *inset = GetCellInset(cell);
1296     int row;
1297
1298     for(int i=0; i < par->Last(); ++i) {
1299         if (par->IsNewline(i)) {
1300             ++cell;
1301             if (cell > GetNumberOfCells()) {
1302                 lyxerr << "Some error in reading old table format occured!" <<
1303                     endl << "Terminating when reading cell[" << cell << "]!" <<
1304                     endl;
1305                 return;
1306             }
1307             row = row_of_cell(cell);
1308             if (cont_row_info[row]) {
1309                 DeleteRow(row);
1310                 cont_row_info.erase(cont_row_info.begin() + row); //&cont_row_info[row]);
1311                 while(!IsFirstCellInRow(--cell));
1312             } else {
1313                 inset = GetCellInset(cell);
1314                 continue;
1315             }
1316             inset = GetCellInset(cell);
1317             row = row_of_cell(cell);
1318             if (!cell_info[row_of_cell(cell)][column_of_cell(cell)].linebreaks)
1319             {
1320                 // insert a space instead
1321                 par->Erase(i);
1322                 par->InsertChar(i, ' ');
1323             }
1324         }
1325         par->CopyIntoMinibuffer(i);
1326         inset->par->InsertFromMinibuffer(inset->par->Last());
1327     }
1328     Reinit();
1329 }
1330
1331
1332 char const * LyXTabular::GetDocBookAlign(int cell, bool isColumn) const
1333 {
1334         int i = isColumn ? cell : column_of_cell(cell);
1335         
1336         //if (isColumn)
1337         //i = cell;
1338         //else
1339         //i = column_of_cell(cell);
1340     if (!isColumn && IsMultiColumn(cell)) {
1341        if (!cellinfo_of_cell(cell)->align_special.empty()) {
1342            return cellinfo_of_cell(cell)->align_special.c_str();
1343        } else {
1344            switch (GetAlignment(cell)) {
1345            case LYX_ALIGN_LEFT:
1346                return "left";
1347            case LYX_ALIGN_RIGHT:
1348                return "right";
1349            default:
1350                return "center";
1351            }
1352        }
1353     } else {
1354        if (!column_info[i].align_special.empty()) {
1355            return column_info[i].align_special.c_str();
1356        }
1357 #ifdef IGNORE_THIS_FOR_NOW
1358        else if (!column_info[i].p_width.empty()) {
1359            file += "p{";
1360            file += column_info[i].p_width;
1361            file += '}';
1362        }
1363 #endif
1364        else {
1365            switch (column_info[i].alignment) {
1366            case LYX_ALIGN_LEFT:
1367                return "left";
1368            case LYX_ALIGN_RIGHT:
1369                return "right";
1370            default:
1371                return "center";
1372            }
1373        }
1374     }
1375 }
1376
1377
1378 // cell <0 will tex the preamble
1379 // returns the number of printed newlines
1380 int LyXTabular::DocBookEndOfCell(ostream & os, int cell, int & depth) const
1381 {
1382     int i;
1383     int ret = 0;
1384     //int tmp; // tmp2; // unused
1385     int nvcell; // fcell; // unused
1386     if (IsLastCell(cell)) {
1387             os << newlineAndDepth(--depth)
1388                << "</ENTRY>"
1389                << newlineAndDepth(--depth)
1390                << "</ROW>"
1391                << newlineAndDepth(--depth)
1392                << "</TBODY>"
1393                << newlineAndDepth(--depth);
1394         if (is_long_tabular)
1395                 os << "</TGROUP>";
1396         else
1397                 os << "</TGROUP>"
1398                    << newlineAndDepth(--depth);
1399         ret += 4;
1400     } else {
1401         nvcell = cell + 1;
1402         if (cell < 0) {
1403             // preamble
1404             if (is_long_tabular)
1405                     os << "<TGROUP ";
1406             else
1407                     os << "<TGROUP ";
1408             os << "COLS='"
1409                << columns_
1410                << "' COLSEP='1' ROWSEP='1'>"
1411                << newlineAndDepth(++depth);
1412             ++ret;
1413             for (i = 0; i < columns_; ++i) {
1414                     os << "<COLSPEC ALIGN='"
1415                        << GetDocBookAlign(i, true)
1416                        << "' COLNAME='col"
1417                        << i + 1
1418                        << "' COLNUM='"
1419                        << i + 1
1420                        << "' COLSEP='";
1421                if (i == (columns_-1)) {
1422                        os << '1';
1423                } else {
1424                    if (column_info[i].right_line ||
1425                        column_info[i+1].left_line)
1426                            os << '1';
1427                    else
1428                            os << '0';
1429                }
1430                os << "'>"
1431                   << newlineAndDepth(depth);
1432                 ++ret;
1433 #ifdef NOT_HANDLED_YET_AS_I_DONT_KNOW_HOW
1434                 if (column_info[i].left_line)
1435                         os << '|';
1436 #endif
1437             }
1438             os << "<TBODY>"
1439                << newlineAndDepth(++depth)
1440                << "<ROW>"
1441                << newlineAndDepth(++depth)
1442                << "<ENTRY ALIGN='"
1443                << GetDocBookAlign(0)
1444                << "'";
1445            if (IsMultiColumn(0)) {
1446                    os << " NAMEST='col1' NAMEEND='col"
1447                       << cells_in_multicolumn(0)
1448                       << "'";
1449            }
1450            os << ">"
1451               << newlineAndDepth(++depth);
1452             ret += 3;
1453         } else {
1454             if (IsLastCellInRow(cell)) {
1455                     os << newlineAndDepth(--depth)
1456                        << "</ENTRY>"
1457                        << newlineAndDepth(--depth)
1458                        << "</ROW>"
1459                        << newlineAndDepth(depth)
1460                        << "<ROW>"
1461                        << newlineAndDepth(++depth)
1462                        << "<ENTRY ALIGN='"
1463                        << GetDocBookAlign(cell + 1)
1464                        << "' VALIGN='middle'";
1465                if (IsMultiColumn(cell + 1)) {
1466                        os << " NAMEST='col"
1467                           << column_of_cell(cell+1) + 1
1468                           << "' NAMEEND='col"
1469                           << column_of_cell(cell + 1) +
1470                                cells_in_multicolumn(cell + 1)
1471                           << "'";
1472                }
1473                os << ">"
1474                   << newlineAndDepth(++depth);
1475                 ret += 4;
1476             } else {
1477                     os << newlineAndDepth(--depth)
1478                        << "</ENTRY>"
1479                        << newlineAndDepth(depth)
1480                        << "<ENTRY ALIGN='"
1481                        << GetDocBookAlign(cell + 1)
1482                        << "' VALIGN='middle'";
1483                if (IsMultiColumn(cell + 1)) {
1484                        os << " NAMEST='col"
1485                           << column_of_cell(cell+1) + 1
1486                           << "' NAMEEND='col"
1487                           << column_of_cell(cell+1) +
1488                                cells_in_multicolumn(cell+1)
1489                           << "'";
1490                }
1491                os << ">"
1492                   << newlineAndDepth(++depth);
1493                 ret += 3;
1494             }
1495         }
1496     }
1497     return ret;
1498 }
1499
1500
1501 bool LyXTabular::IsMultiColumn(int cell) const
1502 {
1503     return (cellinfo_of_cell(cell)->multicolumn != LyXTabular::CELL_NORMAL);
1504 }
1505
1506
1507 LyXTabular::cellstruct * LyXTabular::cellinfo_of_cell(int cell) const
1508 {
1509     int row = row_of_cell(cell);
1510     int column = column_of_cell(cell);
1511     return  &cell_info[row][column];
1512 }
1513    
1514
1515 void LyXTabular::SetMultiColumn(int cell, int number)
1516 {
1517     cellinfo_of_cell(cell)->multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
1518     cellinfo_of_cell(cell)->alignment = column_info[column_of_cell(cell)].alignment;
1519     cellinfo_of_cell(cell)->top_line = row_info[row_of_cell(cell)].top_line;
1520     cellinfo_of_cell(cell)->bottom_line = row_info[row_of_cell(cell)].bottom_line;
1521     for (number--; number > 0; --number) {
1522         cellinfo_of_cell(cell+number)->multicolumn = CELL_PART_OF_MULTICOLUMN;
1523     }
1524     set_row_column_number_info();
1525 }
1526
1527
1528 int LyXTabular::cells_in_multicolumn(int cell) const
1529 {
1530     int row = row_of_cell(cell);
1531     int column = column_of_cell(cell);
1532     int result = 1;
1533     ++column;
1534     while ((column < columns_) &&
1535            cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN)
1536     {
1537         ++result;
1538         ++column;
1539     }
1540     return result;
1541 }
1542
1543
1544 int LyXTabular::UnsetMultiColumn(int cell)
1545 {
1546     int row = row_of_cell(cell);
1547     int column = column_of_cell(cell);
1548     
1549     int result = 0;
1550     
1551     if (cell_info[row][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) {
1552         cell_info[row][column].multicolumn = CELL_NORMAL;
1553         ++column;
1554         while ((column < columns_) &&
1555                (cell_info[row][column].multicolumn ==CELL_PART_OF_MULTICOLUMN))
1556         {
1557             cell_info[row][column].multicolumn = CELL_NORMAL;
1558             ++column;
1559             ++result;
1560         }
1561     }
1562     set_row_column_number_info();
1563     return result;
1564 }
1565
1566
1567 void LyXTabular::SetLongTabular(int what)
1568 {
1569     is_long_tabular = what;
1570 }
1571
1572
1573 bool LyXTabular::IsLongTabular() const
1574 {
1575     return is_long_tabular;
1576 }
1577
1578
1579 void LyXTabular::SetRotateTabular(int what)
1580 {
1581     rotate = what;
1582 }
1583
1584
1585 bool LyXTabular::GetRotateTabular() const
1586 {
1587     return rotate;
1588 }
1589
1590
1591 void LyXTabular::SetRotateCell(int cell, int what)
1592 {
1593     cellinfo_of_cell(cell)->rotate = what;
1594 }
1595
1596
1597 bool LyXTabular::GetRotateCell(int cell) const
1598 {
1599     return cellinfo_of_cell(cell)->rotate;
1600 }
1601
1602
1603 bool LyXTabular::NeedRotating() const
1604 {
1605     if (rotate)
1606         return true;
1607     for (int i = 0; i < rows_; ++i) {
1608         for (int j = 0; j < columns_; ++j) {
1609             if (cell_info[i][j].rotate)
1610                 return true;
1611         }
1612     }
1613     return false;
1614 }
1615
1616
1617 bool LyXTabular::IsLastCell(int cell) const
1618 {
1619     if ((cell+1) < GetNumberOfCells())
1620         return false;
1621     return true;
1622 }
1623
1624
1625 int LyXTabular::GetCellAbove(int cell) const
1626 {
1627     if (row_of_cell(cell) > 0)
1628         return cell_info[row_of_cell(cell)-1][column_of_cell(cell)].cellno;
1629     return cell;
1630 }
1631
1632
1633 int LyXTabular::GetCellBelow(int cell) const
1634 {
1635     if (row_of_cell(cell)+1 < rows_)
1636         return cell_info[row_of_cell(cell)+1][column_of_cell(cell)].cellno;
1637     return cell;
1638 }
1639
1640
1641 int LyXTabular::GetLastCellAbove(int cell) const
1642 {
1643     if (row_of_cell(cell) <= 0)
1644         return cell;
1645     if (!IsMultiColumn(cell))
1646         return GetCellAbove(cell);
1647     return cell_info[row_of_cell(cell)-1][right_column_of_cell(cell)].cellno;
1648 }
1649
1650
1651 int LyXTabular::GetLastCellBelow(int cell) const
1652 {
1653     if (row_of_cell(cell)+1 >= rows_)
1654         return cell;
1655     if (!IsMultiColumn(cell))
1656         return GetCellBelow(cell);
1657     return cell_info[row_of_cell(cell)+1][right_column_of_cell(cell)].cellno;
1658 }
1659
1660
1661 int LyXTabular::GetCellNumber(int row, int column) const
1662 {
1663     if (column >= columns_)
1664         column = columns_ - 1;
1665     else if (column < 0)
1666         column = 0;
1667     if (row >= rows_)
1668         row = rows_ - 1;
1669     else if (row < 0)
1670         row = 0;
1671     
1672     return cell_info[row][column].cellno;
1673 }
1674
1675
1676 void LyXTabular::SetLinebreaks(int cell, bool what)
1677 {
1678     cellinfo_of_cell(cell)->linebreaks = what;
1679 }
1680
1681
1682 bool LyXTabular::GetLinebreaks(int cell) const
1683 {
1684     if (column_info[column_of_cell(cell)].p_width.empty() &&
1685         !(IsMultiColumn(cell) && !cellinfo_of_cell(cell)->p_width.empty()))
1686         return false;
1687     return cellinfo_of_cell(cell)->linebreaks;
1688 }
1689
1690
1691 void LyXTabular::SetLTHead(int cell, bool first)
1692 {
1693     int row = row_of_cell(cell);
1694
1695     if (first) {
1696         if (row == endfirsthead)
1697             endfirsthead = -1;
1698         else
1699             endfirsthead = row;
1700     } else {
1701         if (row == endhead)
1702             endhead = -1;
1703         else
1704             endhead = row;
1705     }
1706 }
1707
1708
1709 bool LyXTabular::GetRowOfLTHead(int cell) const
1710 {
1711     if ((endhead+1) > rows_)
1712         return false;
1713     return (row_of_cell(cell) == endhead);
1714 }
1715
1716
1717 bool LyXTabular::GetRowOfLTFirstHead(int cell) const
1718 {
1719     if ((endfirsthead+1) > rows_)
1720         return false;
1721     return (row_of_cell(cell) == endfirsthead);
1722 }
1723
1724
1725 void LyXTabular::SetLTFoot(int cell, bool last)
1726 {
1727     int row = row_of_cell(cell);
1728
1729     if (last) {
1730         if (row == endlastfoot)
1731             endlastfoot = -1;
1732         else
1733             endlastfoot = row;
1734     } else {
1735         if (row == endfoot)
1736             endfoot = -1;
1737         else
1738             endfoot = row;
1739     }
1740 }
1741
1742
1743 bool LyXTabular::GetRowOfLTFoot(int cell) const
1744 {
1745     if ((endfoot+1) > rows_)
1746         return false;
1747     return (row_of_cell(cell) == endfoot);
1748 }
1749
1750 bool LyXTabular::GetRowOfLTLastFoot(int cell) const
1751 {
1752     if ((endlastfoot+1) > rows_)
1753         return false;
1754     return (row_of_cell(cell) == endlastfoot);
1755 }
1756
1757
1758 void LyXTabular::SetLTNewPage(int cell, bool what)
1759 {
1760     row_info[row_of_cell(cell)].newpage = what;
1761 }
1762
1763
1764 bool LyXTabular::GetLTNewPage(int cell) const
1765 {
1766     return row_info[row_of_cell(cell)].newpage;
1767 }
1768
1769
1770 void LyXTabular::SetAscentOfRow(int row, int height)
1771 {
1772     if (row >= rows_)
1773         return;
1774     row_info[row].ascent_of_row = height;
1775 }
1776
1777
1778 void LyXTabular::SetDescentOfRow(int row, int height)
1779 {
1780     if (row >= rows_)
1781         return;
1782     row_info[row].descent_of_row = height;
1783 }
1784
1785
1786 int LyXTabular::GetAscentOfRow(int row) const
1787 {
1788     if (row >= rows_)
1789         return 0;
1790     return row_info[row].ascent_of_row;
1791 }
1792
1793
1794 int LyXTabular::GetDescentOfRow(int row) const
1795 {
1796     if (row >= rows_)
1797         return 0;
1798     return row_info[row].descent_of_row;
1799 }
1800
1801
1802 int LyXTabular::GetHeightOfTabular() const
1803 {
1804     int height = 0;
1805
1806     for(int row = 0; row < rows_; ++row)
1807         height += GetAscentOfRow(row) + GetDescentOfRow(row) +
1808             GetAdditionalHeight(GetCellNumber(row, 0));
1809     return height;
1810 }
1811
1812
1813 bool LyXTabular::IsPartOfMultiColumn(int row, int column) const
1814 {
1815     if ((row >= rows_) || (column >= columns_))
1816         return false;
1817     return (cell_info[row][column].multicolumn==CELL_PART_OF_MULTICOLUMN);
1818 }
1819
1820
1821 int LyXTabular::TeXTopHLine(ostream & os, int row) const
1822 {
1823     int fcell = GetFirstCellInRow(row);
1824     int n = NumberOfCellsInRow(fcell) + fcell;
1825     int tmp=0;
1826     int i;
1827
1828     for (i = fcell; i < n; ++i) {
1829         if (TopLine(i))
1830             ++tmp;
1831     }
1832     if (tmp == (n - fcell)){
1833         os << "\\hline ";
1834     } else {
1835         for (i = fcell; i < n; ++i) {
1836             if (TopLine(i)) {
1837                 os << "\\cline{"
1838                    << column_of_cell(i) + 1
1839                    << '-'
1840                    << right_column_of_cell(i) + 1
1841                    << "} ";
1842             }
1843         }
1844     }
1845     if (tmp) {
1846         os << endl;
1847         return 1;
1848     }
1849     return 0;
1850 }
1851
1852
1853 int LyXTabular::TeXBottomHLine(ostream & os, int row) const
1854 {
1855     int fcell = GetFirstCellInRow(row);
1856     int n = NumberOfCellsInRow(fcell) + fcell;
1857     int tmp = 0;
1858     int i;
1859
1860     for (i = fcell; i < n; ++i) {
1861         if (BottomLine(i))
1862             ++tmp;
1863     }
1864     if (tmp == (n-fcell)){
1865         os << "\\hline";
1866     } else {
1867         for (i = fcell; i < n; ++i) {
1868             if (BottomLine(i)) {
1869                 os << "\\cline{"
1870                    << column_of_cell(i) + 1
1871                    << '-'
1872                    << right_column_of_cell(i) + 1
1873                    << "} ";
1874             }
1875         }
1876     }
1877     if (tmp) {
1878         os << endl;
1879         return 1;
1880     }
1881     return 0;
1882 }
1883
1884
1885 int LyXTabular::TeXCellPreamble(ostream & os, int cell) const
1886 {
1887     int ret = 0;
1888
1889     if (GetRotateCell(cell)) {
1890         os << "\\begin{sideways}" << endl;
1891         ++ret;
1892     }
1893     if (IsMultiColumn(cell)) {
1894         os << "\\multicolumn{" << cells_in_multicolumn(cell) << "}{";
1895         if (!cellinfo_of_cell(cell+1)->align_special.empty()) {
1896             os << cellinfo_of_cell(cell+1)->align_special << "}{";
1897         } else {
1898             if (LeftLine(cell))
1899                 os << '|';
1900             if (!GetPWidth(cell).empty()) {
1901                 os << "p{" << GetPWidth(cell) << '}';
1902             } else {
1903                 switch (GetAlignment(cell)) {
1904                 case LYX_ALIGN_LEFT:
1905                     os << 'l';
1906                     break;
1907                 case LYX_ALIGN_RIGHT:
1908                     os << 'r';
1909                     break;
1910                 default:
1911                     os << 'c';
1912                     break;
1913                 }
1914             }
1915             if (RightLine(cell))
1916                 os << '|';
1917             if (((cell + 1) < numberofcells) && !IsFirstCellInRow(cell+1) &&
1918                 LeftLine(cell+1))
1919                 os << '|';
1920             os << "}{";
1921         }
1922     }
1923     if (GetLinebreaks(cell)) {
1924         os << "\\parbox[t]{" << GetPWidth(cell) << "}{\\smallskip{}";
1925     }
1926     return ret;
1927 }
1928
1929
1930 int LyXTabular::TeXCellPostamble(ostream & os, int cell) const
1931 {
1932     int ret = 0;
1933
1934     // usual cells
1935     if (GetLinebreaks(cell))
1936         os << "\\smallskip{}}";
1937     if (IsMultiColumn(cell)){
1938         os << '}';
1939     }
1940     if (GetRotateCell(cell)) {
1941         os << endl << "\\end{sideways}";
1942         ++ret;
1943     }
1944     return ret;
1945 }
1946
1947
1948 int LyXTabular::Latex(ostream & os, bool fragile, bool fp) const
1949 {
1950     int ret = 0;
1951     int i,j;
1952     int cell = 0;
1953
1954     //+---------------------------------------------------------------------
1955     //+                      first the opening preamble                    +
1956     //+---------------------------------------------------------------------
1957
1958     if (rotate) {
1959         os << "\\begin{sideways}" << endl;
1960         ++ret;
1961     }
1962     if (is_long_tabular)
1963         os << "\\begin{longtable}{";
1964     else
1965         os << "\\begin{tabular}{";
1966     for (i = 0; i < columns_; ++i) {
1967         if (column_info[i].left_line)
1968             os << '|';
1969         if (!column_info[i].align_special.empty()) {
1970             os << column_info[i].align_special;
1971         } else if (!column_info[i].p_width.empty()) {
1972             os << "p{"
1973                << column_info[i].p_width
1974                << '}';
1975         } else {
1976             switch (column_info[i].alignment) {
1977             case LYX_ALIGN_LEFT:
1978                 os << 'l';
1979                 break;
1980             case LYX_ALIGN_RIGHT:
1981                 os << 'r';
1982                 break;
1983             default:
1984                 os << 'c';
1985                 break;
1986             }
1987         }
1988         if (column_info[i].right_line)
1989             os << '|';
1990     }
1991     os << "}" << endl;
1992     ++ret;
1993
1994     //+---------------------------------------------------------------------
1995     //+                      the single row and columns (cells)            +
1996     //+---------------------------------------------------------------------
1997
1998     for(i=0; i < rows_; ++i) {
1999         ret += TeXTopHLine(os, i);
2000         for(j=0; j < columns_; ++j) {
2001             if (IsPartOfMultiColumn(i,j))
2002                 continue;
2003             ret += TeXCellPreamble(os, cell);
2004             ret += GetCellInset(cell)->Latex(os, fragile, fp);
2005             ret += TeXCellPostamble(os, cell);
2006             if (!IsLastCellInRow(cell)) { // not last cell in row
2007                 os << "&" << endl;
2008                 ++ret;
2009             }
2010             ++cell;
2011         }
2012         os << "\\\\" << endl;
2013         ret += TeXBottomHLine(os, i);
2014         if (IsLongTabular()) {
2015             if (i == endhead) {
2016                 os << "\\endhead\n";
2017                 ++ret;
2018             }
2019             if (i == endfirsthead) {
2020                 os << "\\endfirsthead\n";
2021                 ++ret;
2022             }
2023             if (i == endfoot) {
2024                 os << "\\endfoot\n";
2025                 ++ret;
2026             }
2027             if (i == endlastfoot) {
2028                 os << "\\endlastfoot\n";
2029                 ++ret;
2030             }
2031             if (row_info[i].newpage) {
2032                 os << "\\newpage\n";
2033                 ++ret;
2034             }
2035         }
2036     }
2037
2038     //+---------------------------------------------------------------------
2039     //+                      the closing of the tabular                    +
2040     //+---------------------------------------------------------------------
2041
2042     if (is_long_tabular)
2043         os << "\\end{longtable}";
2044     else
2045         os << "\\end{tabular}";
2046     if (rotate) {
2047         os << "\n\\end{sideways}";
2048         ++ret;
2049     }
2050
2051     return ret;
2052 }
2053
2054
2055 InsetText * LyXTabular::GetCellInset(int cell) const
2056 {
2057     return cell_info[row_of_cell(cell)][column_of_cell(cell)].inset;
2058 }
2059
2060 void LyXTabular::Validate(LaTeXFeatures & features) const
2061 {
2062     if (IsLongTabular())
2063         features.longtable = true;
2064     if (NeedRotating())
2065         features.rotating = true;
2066     for(int cell = 0; cell < numberofcells; ++cell)
2067         GetCellInset(cell)->Validate(features);
2068 }