]> git.lyx.org Git - lyx.git/blob - src/tabular.C
c63b540191a289f20ae41b2e0c54b0101d25e6e1
[lyx.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             lex.pushToken(token);
1317             break;
1318         }
1319         if (owner_->BufferOwner()->parseSingleLyXformat2Token(lex, par,
1320                                                               return_par,
1321                                                               token, pos,
1322                                                               depth, font,
1323                                                               footnoteflag,
1324                                                               footnotekind))
1325         {
1326             // the_end read
1327             lex.pushToken(token);
1328             break;
1329         }
1330         if (return_par) {
1331             lex.printError("New Paragraph allocated! This should not happen!");
1332             lex.pushToken(token);
1333             delete par;
1334             par = return_par;
1335             break;
1336         }
1337     }
1338     // now we have the par we should fill the insets with this!
1339     int cell = 0;
1340     InsetText *inset = GetCellInset(cell);
1341     int row;
1342
1343     for(int i=0; i < par->Last(); ++i) {
1344         if (par->IsNewline(i)) {
1345             ++cell;
1346             if (cell > GetNumberOfCells()) {
1347                 lyxerr << "Some error in reading old table format occured!" <<
1348                     endl << "Terminating when reading cell[" << cell << "]!" <<
1349                     endl;
1350                 return;
1351             }
1352             row = row_of_cell(cell);
1353             if (cont_row_info[row]) {
1354                 DeleteRow(row);
1355                 cont_row_info.erase(cont_row_info.begin() + row); //&cont_row_info[row]);
1356                 while(!IsFirstCellInRow(--cell));
1357             } else {
1358                 inset = GetCellInset(cell);
1359                 continue;
1360             }
1361             inset = GetCellInset(cell);
1362             row = row_of_cell(cell);
1363             if (!cell_info[row_of_cell(cell)][column_of_cell(cell)].linebreaks)
1364             {
1365                 // insert a space instead
1366                 par->Erase(i);
1367                 par->InsertChar(i, ' ');
1368             }
1369         }
1370         par->CopyIntoMinibuffer(i);
1371         inset->par->InsertFromMinibuffer(inset->par->Last());
1372     }
1373     Reinit();
1374 }
1375
1376
1377 char const * LyXTabular::GetDocBookAlign(int cell, bool isColumn) const
1378 {
1379         int i = isColumn ? cell : column_of_cell(cell);
1380         
1381         //if (isColumn)
1382         //i = cell;
1383         //else
1384         //i = column_of_cell(cell);
1385     if (!isColumn && IsMultiColumn(cell)) {
1386        if (!cellinfo_of_cell(cell)->align_special.empty()) {
1387            return cellinfo_of_cell(cell)->align_special.c_str();
1388        } else {
1389            switch (GetAlignment(cell)) {
1390            case LYX_ALIGN_LEFT:
1391                return "left";
1392            case LYX_ALIGN_RIGHT:
1393                return "right";
1394            default:
1395                return "center";
1396            }
1397        }
1398     } else {
1399        if (!column_info[i].align_special.empty()) {
1400            return column_info[i].align_special.c_str();
1401        }
1402 #ifdef IGNORE_THIS_FOR_NOW
1403        else if (!column_info[i].p_width.empty()) {
1404            file += "p{";
1405            file += column_info[i].p_width;
1406            file += '}';
1407        }
1408 #endif
1409        else {
1410            switch (column_info[i].alignment) {
1411            case LYX_ALIGN_LEFT:
1412                return "left";
1413            case LYX_ALIGN_RIGHT:
1414                return "right";
1415            default:
1416                return "center";
1417            }
1418        }
1419     }
1420 }
1421
1422
1423 // cell <0 will tex the preamble
1424 // returns the number of printed newlines
1425 int LyXTabular::DocBookEndOfCell(ostream & os, int cell, int & depth) const
1426 {
1427     int i;
1428     int ret = 0;
1429     //int tmp; // tmp2; // unused
1430     int nvcell; // fcell; // unused
1431     if (IsLastCell(cell)) {
1432             os << newlineAndDepth(--depth)
1433                << "</ENTRY>"
1434                << newlineAndDepth(--depth)
1435                << "</ROW>"
1436                << newlineAndDepth(--depth)
1437                << "</TBODY>"
1438                << newlineAndDepth(--depth);
1439         if (is_long_tabular)
1440                 os << "</TGROUP>";
1441         else
1442                 os << "</TGROUP>"
1443                    << newlineAndDepth(--depth);
1444         ret += 4;
1445     } else {
1446         nvcell = cell + 1;
1447         if (cell < 0) {
1448             // preamble
1449             if (is_long_tabular)
1450                     os << "<TGROUP ";
1451             else
1452                     os << "<TGROUP ";
1453             os << "COLS='"
1454                << columns_
1455                << "' COLSEP='1' ROWSEP='1'>"
1456                << newlineAndDepth(++depth);
1457             ++ret;
1458             for (i = 0; i < columns_; ++i) {
1459                     os << "<COLSPEC ALIGN='"
1460                        << GetDocBookAlign(i, true)
1461                        << "' COLNAME='col"
1462                        << i + 1
1463                        << "' COLNUM='"
1464                        << i + 1
1465                        << "' COLSEP='";
1466                if (i == (columns_-1)) {
1467                        os << '1';
1468                } else {
1469                    if (column_info[i].right_line ||
1470                        column_info[i+1].left_line)
1471                            os << '1';
1472                    else
1473                            os << '0';
1474                }
1475                os << "'>"
1476                   << newlineAndDepth(depth);
1477                 ++ret;
1478 #ifdef NOT_HANDLED_YET_AS_I_DONT_KNOW_HOW
1479                 if (column_info[i].left_line)
1480                         os << '|';
1481 #endif
1482             }
1483             os << "<TBODY>"
1484                << newlineAndDepth(++depth)
1485                << "<ROW>"
1486                << newlineAndDepth(++depth)
1487                << "<ENTRY ALIGN='"
1488                << GetDocBookAlign(0)
1489                << "'";
1490            if (IsMultiColumn(0)) {
1491                    os << " NAMEST='col1' NAMEEND='col"
1492                       << cells_in_multicolumn(0)
1493                       << "'";
1494            }
1495            os << ">"
1496               << newlineAndDepth(++depth);
1497             ret += 3;
1498         } else {
1499             if (IsLastCellInRow(cell)) {
1500                     os << newlineAndDepth(--depth)
1501                        << "</ENTRY>"
1502                        << newlineAndDepth(--depth)
1503                        << "</ROW>"
1504                        << newlineAndDepth(depth)
1505                        << "<ROW>"
1506                        << newlineAndDepth(++depth)
1507                        << "<ENTRY ALIGN='"
1508                        << GetDocBookAlign(cell + 1)
1509                        << "' VALIGN='middle'";
1510                if (IsMultiColumn(cell + 1)) {
1511                        os << " NAMEST='col"
1512                           << column_of_cell(cell+1) + 1
1513                           << "' NAMEEND='col"
1514                           << column_of_cell(cell + 1) +
1515                                cells_in_multicolumn(cell + 1)
1516                           << "'";
1517                }
1518                os << ">"
1519                   << newlineAndDepth(++depth);
1520                 ret += 4;
1521             } else {
1522                     os << newlineAndDepth(--depth)
1523                        << "</ENTRY>"
1524                        << newlineAndDepth(depth)
1525                        << "<ENTRY ALIGN='"
1526                        << GetDocBookAlign(cell + 1)
1527                        << "' VALIGN='middle'";
1528                if (IsMultiColumn(cell + 1)) {
1529                        os << " NAMEST='col"
1530                           << column_of_cell(cell+1) + 1
1531                           << "' NAMEEND='col"
1532                           << column_of_cell(cell+1) +
1533                                cells_in_multicolumn(cell+1)
1534                           << "'";
1535                }
1536                os << ">"
1537                   << newlineAndDepth(++depth);
1538                 ret += 3;
1539             }
1540         }
1541     }
1542     return ret;
1543 }
1544
1545
1546 bool LyXTabular::IsMultiColumn(int cell) const
1547 {
1548     return (cellinfo_of_cell(cell)->multicolumn != LyXTabular::CELL_NORMAL);
1549 }
1550
1551
1552 LyXTabular::cellstruct * LyXTabular::cellinfo_of_cell(int cell) const
1553 {
1554     int row = row_of_cell(cell);
1555     int column = column_of_cell(cell);
1556     return  &cell_info[row][column];
1557 }
1558    
1559
1560 void LyXTabular::SetMultiColumn(int cell, int number)
1561 {
1562     int new_width = cellinfo_of_cell(cell)->width_of_cell;
1563     
1564     cellinfo_of_cell(cell)->multicolumn = LyXTabular::CELL_BEGIN_OF_MULTICOLUMN;
1565     cellinfo_of_cell(cell)->alignment = column_info[column_of_cell(cell)].alignment;
1566     cellinfo_of_cell(cell)->top_line = row_info[row_of_cell(cell)].top_line;
1567     cellinfo_of_cell(cell)->bottom_line = row_info[row_of_cell(cell)].bottom_line;
1568     for (number--; number > 0; --number) {
1569         cellinfo_of_cell(cell+number)->multicolumn = 
1570             LyXTabular::CELL_PART_OF_MULTICOLUMN;
1571         new_width += cellinfo_of_cell(cell+number)->width_of_cell;
1572     }
1573     set_row_column_number_info();
1574     SetWidthOfCell(cell, new_width);
1575 }
1576
1577
1578 int LyXTabular::cells_in_multicolumn(int cell) const
1579 {
1580     int row = row_of_cell(cell);
1581     int column = column_of_cell(cell);
1582     int result = 1;
1583     ++column;
1584     while (column < columns_ && cell_info[row][column].multicolumn
1585            == LyXTabular::CELL_PART_OF_MULTICOLUMN){
1586         ++result;
1587         ++column;
1588     }
1589     return result;
1590 }
1591
1592
1593 int LyXTabular::UnsetMultiColumn(int cell)
1594 {
1595     int row = row_of_cell(cell);
1596     int column = column_of_cell(cell);
1597     
1598     int result = 0;
1599     
1600     if (cell_info[row][column].multicolumn
1601         == LyXTabular::CELL_BEGIN_OF_MULTICOLUMN){
1602         cell_info[row][column].multicolumn = LyXTabular::CELL_NORMAL;
1603         ++column;
1604         while (column < columns_ &&
1605                cell_info[row][column].multicolumn
1606                == LyXTabular::CELL_PART_OF_MULTICOLUMN){
1607             cell_info[row][column].multicolumn = 
1608                 LyXTabular::CELL_NORMAL;
1609             ++column;
1610             ++result;
1611         }
1612     }
1613     set_row_column_number_info();
1614     return result;
1615 }
1616
1617
1618 void LyXTabular::SetLongTabular(int what)
1619 {
1620     is_long_tabular = what;
1621 }
1622
1623
1624 bool LyXTabular::IsLongTabular() const
1625 {
1626     return is_long_tabular;
1627 }
1628
1629
1630 void LyXTabular::SetRotateTabular(int what)
1631 {
1632     rotate = what;
1633 }
1634
1635
1636 bool LyXTabular::GetRotateTabular() const
1637 {
1638     return rotate;
1639 }
1640
1641
1642 void LyXTabular::SetRotateCell(int cell, int what)
1643 {
1644     cellinfo_of_cell(cell)->rotate = what;
1645 }
1646
1647
1648 bool LyXTabular::GetRotateCell(int cell) const
1649 {
1650     return cellinfo_of_cell(cell)->rotate;
1651 }
1652
1653
1654 bool LyXTabular::NeedRotating() const
1655 {
1656     if (rotate)
1657         return true;
1658     for (int i = 0; i < rows_; ++i) {
1659         for (int j = 0; j < columns_; ++j) {
1660             if (cell_info[i][j].rotate)
1661                 return true;
1662         }
1663     }
1664     return false;
1665 }
1666
1667
1668 bool LyXTabular::IsLastCell(int cell) const
1669 {
1670     if (cell < GetNumberOfCells())
1671         return false;
1672     return true;
1673 }
1674
1675
1676 int LyXTabular::GetCellAbove(int cell) const
1677 {
1678     if (row_of_cell(cell) > 0)
1679         return cell_info[row_of_cell(cell)-1][column_of_cell(cell)].cellno;
1680     return cell;
1681 }
1682
1683
1684 int LyXTabular::GetCellNumber(int row, int column) const
1685 {
1686     if (column >= columns_)
1687         column = columns_ - 1;
1688     else if (column < 0)
1689         column = 0;
1690     if (row >= rows_)
1691         row = rows_ - 1;
1692     else if (row < 0)
1693         row = 0;
1694     
1695     return cell_info[row][column].cellno;
1696 }
1697
1698
1699 void LyXTabular::SetLinebreaks(int cell, bool what)
1700 {
1701     cellinfo_of_cell(cell)->linebreaks = what;
1702 }
1703
1704
1705 bool LyXTabular::GetLinebreaks(int cell) const
1706 {
1707     if (column_info[column_of_cell(cell)].p_width.empty() &&
1708         !(IsMultiColumn(cell) && !cellinfo_of_cell(cell)->p_width.empty()))
1709         return false;
1710     return cellinfo_of_cell(cell)->linebreaks;
1711 }
1712
1713
1714 void LyXTabular::SetLTHead(int cell, bool first)
1715 {
1716     int row = row_of_cell(cell);
1717
1718     if (first) {
1719         if (row == endfirsthead)
1720             endfirsthead = -1;
1721         else
1722             endfirsthead = row;
1723     } else {
1724         if (row == endhead)
1725             endhead = -1;
1726         else
1727             endhead = row;
1728     }
1729 }
1730
1731
1732 bool LyXTabular::GetRowOfLTHead(int cell) const
1733 {
1734     if ((endhead+1) > rows_)
1735         return false;
1736     return (row_of_cell(cell) == endhead);
1737 }
1738
1739
1740 bool LyXTabular::GetRowOfLTFirstHead(int cell) const
1741 {
1742     if ((endfirsthead+1) > rows_)
1743         return false;
1744     return (row_of_cell(cell) == endfirsthead);
1745 }
1746
1747
1748 void LyXTabular::SetLTFoot(int cell, bool last)
1749 {
1750     int row = row_of_cell(cell);
1751
1752     if (last) {
1753         if (row == endlastfoot)
1754             endlastfoot = -1;
1755         else
1756             endlastfoot = row;
1757     } else {
1758         if (row == endfoot)
1759             endfoot = -1;
1760         else
1761             endfoot = row;
1762     }
1763 }
1764
1765
1766 bool LyXTabular::GetRowOfLTFoot(int cell) const
1767 {
1768     if ((endfoot+1) > rows_)
1769         return false;
1770     return (row_of_cell(cell) == endfoot);
1771 }
1772
1773 bool LyXTabular::GetRowOfLTLastFoot(int cell) const
1774 {
1775     if ((endlastfoot+1) > rows_)
1776         return false;
1777     return (row_of_cell(cell) == endlastfoot);
1778 }
1779
1780
1781 void LyXTabular::SetLTNewPage(int cell, bool what)
1782 {
1783     row_info[row_of_cell(cell)].newpage = what;
1784 }
1785
1786
1787 bool LyXTabular::GetLTNewPage(int cell) const
1788 {
1789     return row_info[row_of_cell(cell)].newpage;
1790 }
1791
1792
1793 void LyXTabular::SetAscentOfRow(int row, int height)
1794 {
1795     if (row >= rows_)
1796         return;
1797     row_info[row].ascent_of_row = height;
1798 }
1799
1800
1801 void LyXTabular::SetDescentOfRow(int row, int height)
1802 {
1803     if (row >= rows_)
1804         return;
1805     row_info[row].descent_of_row = height;
1806 }
1807
1808
1809 int LyXTabular::GetAscentOfRow(int row) const
1810 {
1811     if (row >= rows_)
1812         return 0;
1813     return row_info[row].ascent_of_row;
1814 }
1815
1816
1817 int LyXTabular::GetDescentOfRow(int row) const
1818 {
1819     if (row >= rows_)
1820         return 0;
1821     return row_info[row].descent_of_row;
1822 }
1823
1824
1825 int LyXTabular::GetHeightOfTabular() const
1826 {
1827     int height = 0;
1828
1829     for(int row = 0; row < rows_; ++row)
1830         height += GetAscentOfRow(row) + GetDescentOfRow(row) +
1831             GetAdditionalHeight(GetCellNumber(row, 0));
1832     return height;
1833 }
1834
1835
1836 bool LyXTabular::IsPartOfMultiColumn(int row, int column) const
1837 {
1838     if ((row >= rows_) || (column >= columns_))
1839         return false;
1840     return (cell_info[row][column].multicolumn==CELL_PART_OF_MULTICOLUMN);
1841 }
1842
1843
1844 int LyXTabular::TeXTopHLine(ostream & os, int row) const
1845 {
1846     int fcell = GetFirstCellInRow(row);
1847     int n = NumberOfCellsInRow(fcell) + fcell;
1848     int tmp=0;
1849     int i;
1850
1851     for (i = fcell; i < n; ++i) {
1852         if (TopLine(i))
1853             ++tmp;
1854     }
1855     if (tmp == (n - fcell)){
1856         os << "\\hline ";
1857     } else {
1858         for (i = fcell; i < n; ++i) {
1859             if (TopLine(i)) {
1860                 os << "\\cline{"
1861                    << column_of_cell(i) + 1
1862                    << '-'
1863                    << right_column_of_cell(i) + 1
1864                    << "} ";
1865             }
1866         }
1867     }
1868     if (tmp) {
1869         os << endl;
1870         return 1;
1871     }
1872     return 0;
1873 }
1874
1875
1876 int LyXTabular::TeXBottomHLine(ostream & os, int row) const
1877 {
1878     int fcell = GetFirstCellInRow(row);
1879     int n = NumberOfCellsInRow(fcell) + fcell;
1880     int tmp = 0;
1881     int i;
1882
1883     for (i = fcell; i < n; ++i) {
1884         if (BottomLine(i))
1885             ++tmp;
1886     }
1887     if (tmp == (n-fcell)){
1888         os << "\\hline";
1889     } else {
1890         for (i = fcell; i < n; ++i) {
1891             if (BottomLine(i)) {
1892                 os << "\\cline{"
1893                    << column_of_cell(i) + 1
1894                    << '-'
1895                    << right_column_of_cell(i) + 1
1896                    << "} ";
1897             }
1898         }
1899     }
1900     if (tmp) {
1901         os << endl;
1902         return 1;
1903     }
1904     return 0;
1905 }
1906
1907
1908 int LyXTabular::TeXCellPreamble(ostream & os, int cell) const
1909 {
1910     int ret = 0;
1911
1912     if (GetRotateCell(cell)) {
1913         os << "\\begin{sideways}" << endl;
1914         ++ret;
1915     }
1916     if (IsMultiColumn(cell)) {
1917         os << "\\multicolumn{" << cells_in_multicolumn(cell) << "}{";
1918         if (!cellinfo_of_cell(cell+1)->align_special.empty()) {
1919             os << cellinfo_of_cell(cell+1)->align_special << "}{";
1920         } else {
1921             if (LeftLine(cell))
1922                 os << '|';
1923             if (!GetPWidth(cell).empty()) {
1924                 os << "p{" << GetPWidth(cell) << '}';
1925             } else {
1926                 switch (GetAlignment(cell)) {
1927                 case LYX_ALIGN_LEFT:
1928                     os << 'l';
1929                     break;
1930                 case LYX_ALIGN_RIGHT:
1931                     os << 'r';
1932                     break;
1933                 default:
1934                     os << 'c';
1935                     break;
1936                 }
1937             }
1938             if (RightLine(cell))
1939                 os << '|';
1940             if (((cell + 1) < numberofcells) && !IsFirstCellInRow(cell+1) &&
1941                 LeftLine(cell+1))
1942                 os << '|';
1943             os << "}{";
1944         }
1945     }
1946     if (GetLinebreaks(cell)) {
1947         os << "\\parbox[t]{" << GetPWidth(cell) << "}{\\smallskip{}";
1948     }
1949     return ret;
1950 }
1951
1952
1953 int LyXTabular::TeXCellPostamble(ostream & os, int cell) const
1954 {
1955     int ret = 0;
1956
1957     // usual cells
1958     if (GetLinebreaks(cell))
1959         os << "\\smallskip{}}";
1960     if (IsMultiColumn(cell)){
1961         os << '}';
1962     }
1963     if (GetRotateCell(cell)) {
1964         os << endl << "\\end{sideways}";
1965         ++ret;
1966     }
1967     return ret;
1968 }
1969
1970
1971 int LyXTabular::Latex(ostream & os, bool fragile, bool fp) const
1972 {
1973     int ret = 0;
1974     int i,j;
1975     int cell = 0;
1976
1977     //+---------------------------------------------------------------------
1978     //+                      first the opening preamble                    +
1979     //+---------------------------------------------------------------------
1980
1981     if (rotate) {
1982         os << "\\begin{sideways}" << endl;
1983         ++ret;
1984     }
1985     if (is_long_tabular)
1986         os << "\\begin{longtable}{";
1987     else
1988         os << "\\begin{tabular}{";
1989     for (i = 0; i < columns_; ++i) {
1990         if (column_info[i].left_line)
1991             os << '|';
1992         if (!column_info[i].align_special.empty()) {
1993             os << column_info[i].align_special;
1994         } else if (!column_info[i].p_width.empty()) {
1995             os << "p{"
1996                << column_info[i].p_width
1997                << '}';
1998         } else {
1999             switch (column_info[i].alignment) {
2000             case LYX_ALIGN_LEFT:
2001                 os << 'l';
2002                 break;
2003             case LYX_ALIGN_RIGHT:
2004                 os << 'r';
2005                 break;
2006             default:
2007                 os << 'c';
2008                 break;
2009             }
2010         }
2011         if (column_info[i].right_line)
2012             os << '|';
2013     }
2014     os << "}" << endl;
2015     ++ret;
2016
2017     //+---------------------------------------------------------------------
2018     //+                      the single row and columns (cells)            +
2019     //+---------------------------------------------------------------------
2020
2021     for(i=0; i < rows_; ++i) {
2022         ret += TeXTopHLine(os, i);
2023         for(j=0; j < columns_; ++j) {
2024             if (IsPartOfMultiColumn(i,j))
2025                 continue;
2026             ret += TeXCellPreamble(os, cell);
2027             ret += GetCellInset(cell)->Latex(os, fragile, fp);
2028             ret += TeXCellPostamble(os, cell);
2029             if (!IsLastCellInRow(cell)) { // not last cell in row
2030                 os << "&" << endl;
2031                 ++ret;
2032             }
2033             ++cell;
2034         }
2035         os << "\\\\" << endl;
2036         ret += TeXBottomHLine(os, i);
2037         if (IsLongTabular()) {
2038             if (i == endhead) {
2039                 os << "\\endhead\n";
2040                 ++ret;
2041             }
2042             if (i == endfirsthead) {
2043                 os << "\\endfirsthead\n";
2044                 ++ret;
2045             }
2046             if (i == endfoot) {
2047                 os << "\\endfoot\n";
2048                 ++ret;
2049             }
2050             if (i == endlastfoot) {
2051                 os << "\\endlastfoot\n";
2052                 ++ret;
2053             }
2054             if (row_info[i].newpage) {
2055                 os << "\\newpage\n";
2056                 ++ret;
2057             }
2058         }
2059     }
2060
2061     //+---------------------------------------------------------------------
2062     //+                      the closing of the tabular                    +
2063     //+---------------------------------------------------------------------
2064
2065     if (is_long_tabular)
2066         os << "\\end{longtable}";
2067     else
2068         os << "\\end{tabular}";
2069     if (rotate) {
2070         os << "\n\\end{sideways}";
2071         ++ret;
2072     }
2073
2074     return ret;
2075 }
2076
2077
2078 InsetText * LyXTabular::GetCellInset(int cell) const
2079 {
2080     return cell_info[row_of_cell(cell)][column_of_cell(cell)].inset;
2081 }
2082
2083 void LyXTabular::Validate(LaTeXFeatures & features) const
2084 {
2085     if (IsLongTabular())
2086         features.longtable = true;
2087     if (NeedRotating())
2088         features.rotating = true;
2089     for(int cell = 0; cell < numberofcells; ++cell)
2090         GetCellInset(cell)->Validate(features);
2091 }