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