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