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