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