]> git.lyx.org Git - features.git/blob - src/tabular.C
Jose's DocBook tables patch; Angus latest preference patch.
[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 /// Define a few methods for the inner structs
46
47 LyXTabular::cellstruct::cellstruct() 
48 {
49     cellno = 0;
50     width_of_cell = 0;
51     multicolumn = LyXTabular::CELL_NORMAL;
52     alignment = LYX_ALIGN_CENTER;
53     valignment = LYX_VALIGN_TOP;
54     top_line = true;
55     bottom_line = false;
56     left_line = true;
57     right_line = false;
58     usebox = BOX_NONE;
59     rotate = false;
60 }
61
62
63 LyXTabular::rowstruct::rowstruct() 
64 {
65     top_line = true;
66     bottom_line = false;
67     ascent_of_row = 0;
68     descent_of_row = 0;
69     newpage = false;
70 }
71
72
73 LyXTabular::columnstruct::columnstruct() 
74 {
75     left_line = true;
76     right_line = false;
77     alignment = LYX_ALIGN_CENTER;
78     valignment = LYX_VALIGN_TOP;
79     width_of_column = 0;
80 }
81
82
83 /* konstruktor */
84 LyXTabular::LyXTabular(InsetTabular * inset, int rows_arg, int columns_arg)
85 {
86     owner_ = inset;
87     Init(rows_arg, columns_arg);
88 }
89
90
91 LyXTabular::LyXTabular(InsetTabular * inset, LyXTabular const & lt)
92 {
93     owner_ = inset;
94     Init(lt.rows_, lt.columns_);
95 #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)
96     operator=(lt);
97 }
98
99
100 LyXTabular::LyXTabular(Buffer const * buf, InsetTabular * inset, LyXLex & lex)
101 {
102     owner_ = inset;
103     Read(buf, lex);
104 }
105
106
107 LyXTabular::~LyXTabular()
108 {
109     delete[] rowofcell;
110     delete[] columnofcell;
111 }
112
113
114 LyXTabular & LyXTabular::operator=(LyXTabular const & lt)
115 {
116     // If this and lt is not of the same size we have a serious bug
117     // So then it is ok to throw an exception, or for now
118     // call abort()
119     Assert(rows_ == lt.rows_ && columns_ == lt.columns_);
120
121     cell_info = lt.cell_info;
122     row_info = lt.row_info;
123     column_info = lt.column_info;
124
125     // long tabular stuff
126     SetLongTabular(lt.is_long_tabular);
127     endhead = lt.endhead;
128     endfoot = lt.endfoot;
129     endfirsthead = lt.endfirsthead;
130     endlastfoot = lt.endlastfoot;
131
132     rotate = lt.rotate;
133
134     Reinit();
135     
136     return *this;
137 }
138
139
140 LyXTabular * LyXTabular::Clone(InsetTabular * inset)
141 {
142     LyXTabular * result = new LyXTabular(inset, *this);
143     // don't know if this is good but I need to Clone also
144     // the text-insets here, this is for the Undo-facility!
145     for(int i = 0; i < rows_; ++i) {
146         for(int j = 0; j < columns_; ++j) {
147             result->cell_info[i][j].inset = cell_info[i][j].inset;
148             result->cell_info[i][j].inset.setOwner(inset);
149         }
150     }
151     return result;
152 }
153
154
155 /* activates all lines and sets all widths to 0 */ 
156 void LyXTabular::Init(int rows_arg, int columns_arg)
157 {
158
159     rows_ = rows_arg;
160     columns_ = columns_arg;
161     row_info = row_vector(rows_, rowstruct());
162     column_info = column_vector(columns_, columnstruct());
163     cell_info = cell_vvector(rows_, cell_vector(columns_, cellstruct()));
164
165     // Jürgen, use iterators.
166     int cellno = 0;
167     for (int i = 0; i < rows_; ++i) {
168         for (int j = 0; j < columns_; ++j) {
169             cell_info[i][j].inset.setOwner(owner_);
170             cell_info[i][j].inset.SetDrawFrame(0, InsetText::LOCKED);
171             cell_info[i][j].cellno = cellno++;
172         }
173         cell_info[i][columns_-1].right_line = true;
174     }
175     //row_info[i - 1].bottom_line = true;
176     //row_info[0].bottom_line = true;
177     row_info.back().bottom_line = true;
178     row_info.front().bottom_line = true;
179
180     for (int i = 0; i < columns_; ++i) {
181         calculate_width_of_column(i);
182     }
183     //column_info[columns_ - 1].right_line = true;
184     column_info.back().right_line = true;
185    
186     calculate_width_of_tabular();
187
188     rowofcell = 0;
189     columnofcell = 0;
190     set_row_column_number_info();
191     is_long_tabular = false;
192     rotate = false;
193     endhead = 0;
194     endfirsthead = 0;
195     endfoot = 0;
196     endlastfoot = 0;
197 }
198
199
200 void LyXTabular::AppendRow(int cell)
201 {
202     ++rows_;
203    
204     int row = row_of_cell(cell);
205
206     row_vector::iterator rit = row_info.begin() + row;
207     row_info.insert(rit, rowstruct());
208
209 #if 0
210     cell_vvector::iterator cit = cell_info.begin() + row;
211     cell_info.insert(cit, vector<cellstruct>(columns_, cellstruct()));
212 #else
213     cell_vvector c_info = cell_vvector(rows_, cell_vector(columns_,
214                                                           cellstruct()));
215
216     for(int i = 0; i <= row; ++i) {
217         for(int j = 0; j < columns_; ++j) {
218             c_info[i][j] = cell_info[i][j];
219         }
220     }
221     for(int i = row + 1; i < rows_; ++i) {
222         for(int j = 0; j < columns_; ++j) {
223             c_info[i][j] = cell_info[i-1][j];
224         }
225     }
226     cell_info = c_info;
227     ++row;
228     for (int j = 0; j < columns_; ++j) {
229         cell_info[row][j].inset.clear();
230     }
231 #endif
232     Reinit();
233 }
234
235
236 void LyXTabular::DeleteRow(int row)
237 {
238     // Why make it so hard? (Lgb)
239     //if (!(rows_ - 1))
240     //return;
241     if (rows_ == 1) return; // Not allowed to delete last row
242         
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     column_vector::iterator cit = column_info.begin() + column + 1;
258     column_info.insert(cit, columnstruct());
259
260     for (int i = 0; i < rows_; ++i) {
261         for (int j = 0; j <= column; ++j) {
262             c_info[i][j] = cell_info[i][j];
263         }
264         for (int j = column + 1; j < columns_; ++j) {
265             c_info[i][j] = cell_info[i][j - 1];
266         }
267         // care about multicolumns
268         if (cell_info[i][column + 1].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) {
269             cell_info[i][column + 1].multicolumn = CELL_PART_OF_MULTICOLUMN;
270         }
271         if ((column + 1) == columns_ ||
272             cell_info[i][column + 2].multicolumn != CELL_PART_OF_MULTICOLUMN) {
273             cell_info[i][column + 1].multicolumn = LyXTabular::CELL_NORMAL;
274         }
275     }
276     cell_info = c_info;
277     ++column;
278     for (int i = 0; i < rows_; ++i) {
279         cell_info[i][column].inset.clear();
280     }
281     Reinit();
282 }
283
284
285 void LyXTabular::DeleteColumn(int column)
286 {
287     // Similar to DeleteRow
288     //if (!(columns_ - 1))
289     //return;
290     if (columns_ == 1) return; // Not allowed to delete last column
291          
292     column_info.erase(column_info.begin() + column);
293     for (int i = 0; i < rows_; ++i) {
294         cell_info[i].erase(cell_info[i].begin() + column);
295     }
296     --columns_;
297     Reinit();
298 }
299
300
301 void LyXTabular::Reinit()
302 {   
303     // Jürgen, use iterators.
304     for (int i = 0; i < rows_; ++i) {
305         for (int j = 0; j < columns_; ++j) {
306             cell_info[i][j].width_of_cell = 0;
307             cell_info[i][j].inset.setOwner(owner_);
308         }
309     }
310   
311     for (int i = 0; i < columns_; ++i) {
312         calculate_width_of_column(i);
313     }
314     calculate_width_of_tabular();
315
316     set_row_column_number_info();
317 }
318
319
320 void LyXTabular::set_row_column_number_info(bool oldformat)
321 {
322     int c = 0;
323     int column = 0;
324     numberofcells = -1;
325     int row = 0;
326     for (; row < rows_; ++row) {
327         for (column = 0; column<columns_; ++column) {
328             if (cell_info[row][column].multicolumn
329                 != LyXTabular::CELL_PART_OF_MULTICOLUMN)
330                 ++numberofcells;
331             cell_info[row][column].cellno = numberofcells;
332         }
333     }
334     ++numberofcells; // because this is one more than as we start from 0
335     row = 0;
336     column = 0;
337
338     delete [] rowofcell;
339     rowofcell = new int[numberofcells];
340     delete [] columnofcell;
341     columnofcell = new int[numberofcells];
342   
343     while (c < numberofcells && row < rows_ && column < columns_) {
344         rowofcell[c] = row;
345         columnofcell[c] = column;
346         ++c;
347         do {
348             ++column;
349         } while (column < columns_ &&
350                  cell_info[row][column].multicolumn
351                  == LyXTabular::CELL_PART_OF_MULTICOLUMN);
352         if (column == columns_) {
353             column = 0;
354             ++row;
355         }
356     }
357     for (row = 0; row < rows_; ++row) {
358         for (column = 0; column<columns_; ++column) {
359             if (IsPartOfMultiColumn(row,column))
360                 continue;
361             // now set the right line of multicolumns right for oldformat read
362             if (oldformat &&
363                 cell_info[row][column].multicolumn==CELL_BEGIN_OF_MULTICOLUMN)
364             {
365                 int cn=cells_in_multicolumn(cell_info[row][column].cellno);
366                 cell_info[row][column].right_line =
367                     cell_info[row][column+cn-1].right_line;
368             }
369             cell_info[row][column].inset.SetAutoBreakRows(
370                 !GetPWidth(GetCellNumber(row, column)).empty());
371         }
372     }
373 }
374
375
376 int LyXTabular::GetNumberOfCells() const
377 {
378     return numberofcells;
379 }
380
381
382 int LyXTabular::NumberOfCellsInRow(int cell) const
383 {
384     int const row = row_of_cell(cell);
385     int result = 0;
386     for (int i = 0; i < columns_; ++i) {
387         if (cell_info[row][i].multicolumn != LyXTabular::CELL_PART_OF_MULTICOLUMN)
388             ++result;
389     }
390     return result;
391 }
392
393
394 /* returns 1 if there is a topline, returns 0 if not */ 
395 bool LyXTabular::TopLine(int cell, bool onlycolumn) const
396 {
397     int const row = row_of_cell(cell);
398     
399     if (!onlycolumn && IsMultiColumn(cell))
400         return cellinfo_of_cell(cell)->top_line;
401     return row_info[row].top_line;
402 }
403
404
405 bool LyXTabular::BottomLine(int cell, bool onlycolumn) const
406 {
407     // no bottom line underneath non-existent cells if you please
408     // Isn't that a programming error? Is so this should
409     // be an Assert instead. (Lgb)
410     if(cell >= numberofcells)
411         return false;
412
413     if (!onlycolumn && IsMultiColumn(cell))
414         return cellinfo_of_cell(cell)->bottom_line;
415     return row_info[row_of_cell(cell)].bottom_line;
416 }
417
418
419 bool LyXTabular::LeftLine(int cell, bool onlycolumn) const
420 {
421     if (!onlycolumn && IsMultiColumn(cell))
422         return cellinfo_of_cell(cell)->left_line;
423     return column_info[column_of_cell(cell)].left_line;
424 }
425
426
427 bool LyXTabular::RightLine(int cell, bool onlycolumn) const
428 {
429     if (!onlycolumn && IsMultiColumn(cell))
430         return cellinfo_of_cell(cell)->right_line;
431     return column_info[right_column_of_cell(cell)].right_line;
432 }
433
434
435 bool LyXTabular::TopAlreadyDrawed(int cell) const
436 {
437     if (GetAdditionalHeight(cell))
438         return false;
439     int row = row_of_cell(cell);
440     if (row > 0) {
441         int column = column_of_cell(cell);
442         --row;
443         while (column
444                && cell_info[row][column].multicolumn
445                == LyXTabular::CELL_PART_OF_MULTICOLUMN)
446             --column;
447         if (cell_info[row][column].multicolumn == LyXTabular::CELL_NORMAL)
448             return row_info[row].bottom_line;
449         else
450             return cell_info[row][column].bottom_line;
451     }
452     return false;
453 }
454
455
456 bool LyXTabular::LeftAlreadyDrawed(int cell) const
457 {
458     int column = column_of_cell(cell);
459     if (column > 0) {
460         int row = row_of_cell(cell);
461         while (--column &&
462                (cell_info[row][column].multicolumn ==
463                 LyXTabular::CELL_PART_OF_MULTICOLUMN));
464         if (GetAdditionalWidth(cell_info[row][column].cellno))
465             return false;
466         return column_info[column].right_line;
467     }
468     return false;
469 }
470
471
472 bool LyXTabular::IsLastRow(int cell) const
473 {
474     return (row_of_cell(cell) == rows_ - 1);
475 }
476
477
478 int LyXTabular::GetAdditionalHeight(int cell) const
479 {
480     int const row = row_of_cell(cell);
481     if (!row) return 0;
482
483     bool top = true;
484     bool bottom = true;
485
486     for (int column = 0; column < columns_ - 1 && bottom; ++column) {
487         switch (cell_info[row - 1][column].multicolumn) {
488         case LyXTabular::CELL_BEGIN_OF_MULTICOLUMN:
489             bottom = cell_info[row - 1][column].bottom_line;
490             break;
491         case LyXTabular::CELL_NORMAL:
492             bottom = row_info[row - 1].bottom_line;
493         }
494     }
495     for (int column = 0; column < columns_ - 1 && top; ++column) {
496         switch (cell_info[row][column].multicolumn){
497         case LyXTabular::CELL_BEGIN_OF_MULTICOLUMN:
498             top = cell_info[row][column].top_line;
499             break;
500         case LyXTabular::CELL_NORMAL:
501             top = row_info[row].top_line;
502         }
503     }
504     if (top && bottom)
505         return WIDTH_OF_LINE;
506     return 0;
507 }
508
509
510 int LyXTabular::GetAdditionalWidth(int cell) const
511 {
512     // internally already set in SetWidthOfCell
513     // used to get it back in text.C
514     int const col = right_column_of_cell(cell);
515     if (col < columns_ - 1 && column_info[col].right_line &&
516         column_info[col+1].left_line)
517         return WIDTH_OF_LINE;
518     else
519         return 0;
520 }
521
522
523 // returns the maximum over all rows 
524 int LyXTabular::GetWidthOfColumn(int cell) const
525 {
526     int const column1 = column_of_cell(cell);
527     int const column2 = right_column_of_cell(cell);
528     int result = 0;
529     int i = column1;
530     for (; i <= column2; ++i) {
531         result += column_info[i].width_of_column;
532     }
533     return result;
534 }
535
536
537 int LyXTabular::GetWidthOfTabular() const
538 {
539     return width_of_tabular;
540 }
541
542
543 /* returns 1 if a complete update is necessary, otherwise 0 */ 
544 bool LyXTabular::SetWidthOfMulticolCell(int cell, int new_width)
545 {
546     if (!IsMultiColumn(cell))
547         return false;
548     
549     int const row = row_of_cell(cell);
550     int const column1 = column_of_cell(cell);
551     int const column2 = right_column_of_cell(cell);
552
553     // first set columns to 0 so we can calculate the right width
554     int i = column1;
555     for (; i <= column2; ++i) {
556         cell_info[row][i].width_of_cell = 0;
557     }
558     // set the width to MAX_WIDTH until width > 0
559     int width = (new_width + 2 * WIDTH_OF_LINE);
560     for (i = column1;
561          i < column2 && width > column_info[i].width_of_column;
562          ++i) {
563         cell_info[row][i].width_of_cell = column_info[i].width_of_column;
564         width -= column_info[i].width_of_column;
565     }
566     if (width > 0) {
567         cell_info[row][i].width_of_cell = width;
568     }
569     return true;
570 }
571
572
573 void LyXTabular::recalculateMulticolCells(int cell, int new_width)
574 {
575     int const row = row_of_cell(cell);
576     int const column1 = column_of_cell(cell);
577     int const column2 = right_column_of_cell(cell);
578
579     // first set columns to 0 so we can calculate the right width
580     int i = column1;
581     for (; i <= column2; ++i)
582         cell_info[row][i].width_of_cell = 0;
583     for(i = cell + 1; (i < numberofcells) && (!IsMultiColumn(i)); ++i)
584         ;
585     if (i < numberofcells)
586         recalculateMulticolCells(i, GetWidthOfCell(i) - (2 * WIDTH_OF_LINE));
587     SetWidthOfMulticolCell(cell, new_width);
588 }
589
590
591 /* returns 1 if a complete update is necessary, otherwise 0 */ 
592 bool LyXTabular::SetWidthOfCell(int cell, int new_width)
593 {
594     int const row = row_of_cell(cell);
595     int const column1 = column_of_cell(cell);
596     bool tmp = false;
597     int width = 0;
598
599     if (GetWidthOfCell(cell) == (new_width+2*WIDTH_OF_LINE))
600         return false;
601     if (IsMultiColumn(cell, true)) {
602         tmp = SetWidthOfMulticolCell(cell, new_width);
603     } else {
604         width = (new_width + 2*WIDTH_OF_LINE);
605         cell_info[row][column1].width_of_cell = width;
606         if (column_info[column1].right_line && (column1 < columns_-1) &&
607             column_info[column1+1].left_line) // additional width
608             cell_info[row][column1].width_of_cell += WIDTH_OF_LINE;
609         tmp = calculate_width_of_column_NMC(column1);
610     }
611     if (tmp) {
612         int i = 0;
613         for(; i<columns_; ++i)
614             calculate_width_of_column_NMC(i);
615         for(i = 0; (i < numberofcells) && !IsMultiColumn(i); ++i)
616             ;
617         if (i < numberofcells)
618             recalculateMulticolCells(i, GetWidthOfCell(i)-(2 * WIDTH_OF_LINE));
619         for(i = 0; i < columns_; ++i)
620             calculate_width_of_column(i);
621         calculate_width_of_tabular();
622         return true;
623     }
624     return false;
625 }
626
627
628 bool LyXTabular::SetAlignment(int cell, LyXAlignment align, bool onlycolumn)
629 {
630     if (!IsMultiColumn(cell) || onlycolumn)
631         column_info[column_of_cell(cell)].alignment = align;
632     if (!onlycolumn)
633         cellinfo_of_cell(cell)->alignment = align;
634     return true;
635 }
636
637
638 bool LyXTabular::SetVAlignment(int cell, VAlignment align, bool onlycolumn)
639 {
640     if (!IsMultiColumn(cell) || onlycolumn)
641         column_info[column_of_cell(cell)].valignment = align;
642     if (!onlycolumn)
643         cellinfo_of_cell(cell)->valignment = align;
644     return true;
645 }
646
647
648 bool LyXTabular::SetColumnPWidth(int cell, string const & width)
649 {
650     bool flag = !width.empty();
651     int const j = column_of_cell(cell);
652
653     column_info[j].p_width = width;
654     if (flag) // do this only if there is a width
655         SetAlignment(cell, LYX_ALIGN_LEFT);
656     for(int i = 0; i < rows_; ++i) {
657         int c = GetCellNumber(i, j);
658         flag = !GetPWidth(c).empty(); // because of multicolumns!
659         GetCellInset(c)->SetAutoBreakRows(flag);
660     }
661     return true;
662 }
663
664
665 bool LyXTabular::SetMColumnPWidth(int cell, string const & width)
666 {
667     bool const flag = !width.empty();
668
669     cellinfo_of_cell(cell)->p_width = width;
670     if (IsMultiColumn(cell)) {
671         GetCellInset(cell)->SetAutoBreakRows(flag);
672         return true;
673     }
674     return false;
675 }
676
677
678 bool LyXTabular::SetAlignSpecial(int cell, string const & special,
679                                  LyXTabular::Feature what)
680 {
681     if (what == SET_SPECIAL_MULTI)
682         cellinfo_of_cell(cell)->align_special = special;
683     else
684         column_info[column_of_cell(cell)].align_special = special;
685     return true;
686 }
687
688
689 bool LyXTabular::SetAllLines(int cell, bool line)
690 {
691     SetTopLine(cell, line);
692     SetBottomLine(cell, line);
693     SetRightLine(cell, line);
694     SetLeftLine(cell, line);
695     return true;
696 }
697
698
699 bool LyXTabular::SetTopLine(int cell, bool line, bool onlycolumn)
700 {
701     int const row = row_of_cell(cell);
702
703     if (onlycolumn || !IsMultiColumn(cell))
704         row_info[row].top_line = line;
705     else
706         cellinfo_of_cell(cell)->top_line = line;
707     return true;
708 }
709
710
711 bool LyXTabular::SetBottomLine(int cell, bool line, bool onlycolumn)
712 {
713     if (onlycolumn || !IsMultiColumn(cell))
714         row_info[row_of_cell(cell)].bottom_line = line;
715     else
716         cellinfo_of_cell(cell)->bottom_line = line;
717     return true;
718 }
719
720
721 bool LyXTabular::SetLeftLine(int cell, bool line, bool onlycolumn)
722 {
723     if (onlycolumn || !IsMultiColumn(cell))
724         column_info[column_of_cell(cell)].left_line = line;
725     else
726         cellinfo_of_cell(cell)->left_line = line;
727     return true;
728 }
729
730
731 bool LyXTabular::SetRightLine(int cell, bool line, bool onlycolumn)
732 {
733     if (onlycolumn || !IsMultiColumn(cell))
734         column_info[right_column_of_cell(cell)].right_line = line;
735     else
736         cellinfo_of_cell(cell)->right_line = line;
737     return true;
738 }
739
740
741 LyXAlignment LyXTabular::GetAlignment(int cell, bool onlycolumn) const
742 {
743     if (!onlycolumn && IsMultiColumn(cell))
744         return cellinfo_of_cell(cell)->alignment;
745     else
746         return column_info[column_of_cell(cell)].alignment;
747 }
748
749
750 LyXTabular::VAlignment LyXTabular::GetVAlignment(int cell, bool onlycolumn) const
751 {
752     if (!onlycolumn && IsMultiColumn(cell))
753         return cellinfo_of_cell(cell)->valignment;
754     else
755         return column_info[column_of_cell(cell)].valignment;
756 }
757
758
759 string const LyXTabular::GetPWidth(int cell) const
760 {
761     if (IsMultiColumn(cell))
762         return cellinfo_of_cell(cell)->p_width;
763     return column_info[column_of_cell(cell)].p_width;
764 }
765
766
767 string const LyXTabular::GetColumnPWidth(int cell) const
768 {
769     return column_info[column_of_cell(cell)].p_width;
770 }
771
772
773 string const LyXTabular::GetMColumnPWidth(int cell) const
774 {
775     if (IsMultiColumn(cell))
776         return cellinfo_of_cell(cell)->p_width;
777     return string();
778 }
779
780
781 string const LyXTabular::GetAlignSpecial(int cell, int what) const
782 {
783     if (what == SET_SPECIAL_MULTI)
784         return cellinfo_of_cell(cell)->align_special;
785     return column_info[column_of_cell(cell)].align_special;
786 }
787
788
789 int LyXTabular::GetWidthOfCell(int cell) const
790 {
791     int const row = row_of_cell(cell);
792     int const column1 = column_of_cell(cell);
793     int const column2 = right_column_of_cell(cell);
794     int result = 0;
795     for (int i = column1; i <= column2; ++i) {
796         result += cell_info[row][i].width_of_cell;
797     }
798     return result;
799 }
800
801
802 int LyXTabular::GetBeginningOfTextInCell(int cell) const
803 {
804     int x = 0;
805    
806     switch (GetAlignment(cell)){
807     case LYX_ALIGN_CENTER:
808         x += (GetWidthOfColumn(cell) - GetWidthOfCell(cell)) / 2;
809         break;
810     case LYX_ALIGN_RIGHT:
811         x += GetWidthOfColumn(cell) - GetWidthOfCell(cell);
812         // + GetAdditionalWidth(cell);
813         break;
814     default: /* LYX_ALIGN_LEFT: nothing :-) */ 
815         break;
816     }
817     
818     // the LaTeX Way :-(
819     x += WIDTH_OF_LINE;
820     return x;
821 }
822
823
824 bool LyXTabular::IsFirstCellInRow(int cell) const
825 {
826     return column_of_cell(cell) == 0;
827 }
828
829
830 int LyXTabular::GetFirstCellInRow(int row) const
831 {
832     if (row > (rows_-1))
833         row = rows_ - 1;
834     return cell_info[row][0].cellno;
835 }
836
837 bool LyXTabular::IsLastCellInRow(int cell) const
838 {
839     return (right_column_of_cell(cell) == (columns_ - 1));
840 }
841
842
843 int LyXTabular::GetLastCellInRow(int row) const
844 {
845     if (row > (rows_-1))
846         row = rows_ - 1;
847     return cell_info[row][columns_-1].cellno;
848 }
849
850
851 bool LyXTabular::calculate_width_of_column(int column)
852 {
853     int const old_column_width = column_info[column].width_of_column;
854     int maximum = 0;
855     
856     for (int i = 0; i < rows_; ++i) {
857         maximum = max(cell_info[i][column].width_of_cell, maximum);
858     }
859     column_info[column].width_of_column = maximum;
860     return (column_info[column].width_of_column != old_column_width);
861 }
862
863
864 //
865 // calculate the with of the column without regarding REAL MultiColumn
866 // cells. This means MultiColumn-cells spanning more than 1 column.
867 //
868 bool LyXTabular::calculate_width_of_column_NMC(int column)
869 {
870     int const old_column_width = column_info[column].width_of_column;
871     int max = 0;
872     for (int i = 0; i < rows_; ++i) {
873         if (!IsMultiColumn(GetCellNumber(i, column), true) &&
874             (cell_info[i][column].width_of_cell > max)) {
875             max = cell_info[i][column].width_of_cell;
876         }
877     }
878     column_info[column].width_of_column = max;
879     return (column_info[column].width_of_column != old_column_width);
880 }
881
882
883 void LyXTabular::calculate_width_of_tabular()
884 {
885     width_of_tabular = 0;
886     for (int i = 0; i < columns_; ++i) {
887         width_of_tabular += column_info[i].width_of_column;
888     }
889 }
890
891
892 int LyXTabular::row_of_cell(int cell) const
893 {
894     if (cell >= numberofcells)
895         return rows_-1;
896     else if (cell < 0)
897         return 0;
898     return rowofcell[cell];
899 }
900
901
902 int LyXTabular::column_of_cell(int cell) const
903 {
904     if (cell >= numberofcells)
905         return columns_-1;
906     else if (cell < 0)
907         return 0;
908     return columnofcell[cell];
909 }
910
911
912 int LyXTabular::right_column_of_cell(int cell) const
913 {
914     int const row = row_of_cell(cell);
915     int column = column_of_cell(cell);
916     while (column < (columns_ - 1) &&
917            cell_info[row][column+1].multicolumn == LyXTabular::CELL_PART_OF_MULTICOLUMN)
918         ++column;
919     return column;
920 }
921
922
923 const string write_attribute(const string name, const int value)
924 {
925     string str = " " + name + "=\"" + tostr(value) + "\"";
926     return str;
927 }
928
929
930 const string write_attribute(string name, const string & value)
931 {
932     string str = " " + name + "=\"" + value + "\"";
933     return str;
934 }
935
936
937 const string write_attribute(string name, const bool value)
938 {
939     string str = " " + name + "=\"" + tostr((int)value) + "\"";
940     return str;
941 }
942
943
944 void LyXTabular::Write(Buffer const * buf, ostream & os) const
945 {
946     // header line
947     os << "<LyXTabular" <<
948         write_attribute("version", 1) <<
949         write_attribute("rows", rows_) <<
950         write_attribute("columns", columns_) <<
951         ">\n";
952     // global longtable options
953     os << "<Features" <<
954         write_attribute("rotate", rotate) <<
955         write_attribute("islongtable", is_long_tabular) <<
956         write_attribute("endhead", endhead) <<
957         write_attribute("endfirsthead", endfirsthead) <<
958         write_attribute("endfoot", endfoot) <<
959         write_attribute("endlastfoot", endlastfoot) <<
960         ">\n\n";
961     for (int i = 0; i < rows_; ++i) {
962         os << "<Row" <<
963             write_attribute("topline", row_info[i].top_line) <<
964             write_attribute("bottomline", row_info[i].bottom_line) <<
965             write_attribute("newpage", row_info[i].newpage) <<
966             ">\n";
967         for (int j = 0; j < columns_; ++j) {
968             if (!i) {
969                 os << "<Column" <<
970                     write_attribute("alignment", column_info[j].alignment) <<
971                     write_attribute("valignment", column_info[j].valignment) <<
972                     write_attribute("leftline", column_info[j].left_line) <<
973                     write_attribute("rightline", column_info[j].right_line) <<
974                     write_attribute("width", VSpace(column_info[j].p_width).asLyXCommand()) <<
975                     write_attribute("special", column_info[j].align_special) <<
976                     ">\n";
977             } else {
978                 os << "<Column>\n";
979             }
980             os << "<Cell" <<
981                 write_attribute("multicolumn", cell_info[i][j].multicolumn) <<
982                 write_attribute("alignment", cell_info[i][j].alignment) <<
983                 write_attribute("valignment", cell_info[i][j].valignment) <<
984                 write_attribute("topline", cell_info[i][j].top_line) <<
985                 write_attribute("bottomline", cell_info[i][j].bottom_line) <<
986                 write_attribute("leftline", cell_info[i][j].left_line) <<
987                 write_attribute("rightline", cell_info[i][j].right_line) <<
988                 write_attribute("rotate", cell_info[i][j].rotate) <<
989                 write_attribute("usebox", (int)cell_info[i][j].usebox) <<
990                 write_attribute("width", cell_info[i][j].p_width) <<
991                 write_attribute("special", cell_info[i][j].align_special) <<
992                 ">\n";
993             os << "\\begin_inset ";
994             cell_info[i][j].inset.Write(buf, os);
995             os << "\n\\end_inset \n"
996                << "</Cell>\n"
997                << "</Column>\n";
998         }
999         os << "</Row>\n";
1000     }
1001     os << "</LyXTabular>\n";
1002 }
1003
1004
1005 static
1006 bool getTokenValue(string const str, const char * token, string & ret)
1007 {
1008     int pos = str.find(token);
1009     char ch = str[pos+strlen(token)];
1010
1011     if ((pos < 0) || (ch != '='))
1012         return false;
1013     ret.erase();
1014     pos += strlen(token)+1;
1015     ch = str[pos];
1016     if ((ch != '"') && (ch != '\'')) { // only read till next space
1017         ret += ch;
1018         ch = ' ';
1019     }
1020     while((pos < int(str.length()-1)) && (str[++pos] != ch))
1021         ret += str[pos];
1022
1023     return true;
1024 }
1025
1026
1027 static
1028 bool getTokenValue(string const str, const char * token, int & num)
1029 {
1030     int pos = str.find(token);
1031     char ch = str[pos+strlen(token)];
1032
1033     if ((pos < 0) || (ch != '='))
1034         return false;
1035     string ret;
1036     //ret.erase(); // why? (Lgb)
1037     pos += strlen(token)+1;
1038     ch = str[pos];
1039     if ((ch != '"') && (ch != '\'')) { // only read till next space
1040         if (!isdigit(ch))
1041             return false;
1042         ret += ch;
1043     }
1044     ++pos;
1045     while((pos < int(str.length()-1)) && isdigit(str[pos]))
1046         ret += str[pos++];
1047
1048     num = strToInt(ret);
1049     return true;
1050 }
1051
1052
1053 static
1054 bool getTokenValue(string const str, const char * token, LyXAlignment & num)
1055 {
1056     int tmp;
1057     bool ret = getTokenValue(str, token, tmp);
1058     num = static_cast<LyXAlignment>(tmp);
1059     return ret;
1060 }
1061
1062
1063 static
1064 bool getTokenValue(string const str, const char * token,
1065                    LyXTabular::VAlignment & num)
1066 {
1067     int tmp;
1068     bool ret = getTokenValue(str, token, tmp);
1069     num = static_cast<LyXTabular::VAlignment>(tmp);
1070     return ret;
1071 }
1072
1073
1074 static
1075 bool getTokenValue(string const str, const char * token,
1076                    LyXTabular::BoxType & num)
1077 {
1078     int tmp;
1079     bool ret = getTokenValue(str, token, tmp);
1080     num = static_cast<LyXTabular::BoxType>(tmp);
1081     return ret;
1082 }
1083
1084
1085 static
1086 bool getTokenValue(string const str, const char * token, bool & flag)
1087 {
1088     int pos = str.find(token);
1089     char ch = str[pos+strlen(token)];
1090
1091     if ((pos < 0) || (ch != '='))
1092         return false;
1093     string ret;
1094     //ret.erase(); // Why? (Lgb)
1095     pos += strlen(token)+1;
1096     ch = str[pos];
1097     if ((ch != '"') && (ch != '\'')) { // only read till next space
1098         if (!isdigit(ch))
1099             return false;
1100         ret += ch;
1101     }
1102     ++pos;
1103     while((pos < int(str.length()-1)) && isdigit(str[pos]))
1104         ret += str[pos++];
1105
1106     flag = strToInt(ret);
1107     return true;
1108 }
1109
1110
1111 static inline
1112 void l_getline(istream & is, string & str)
1113 {
1114     getline(is, str);
1115     while(str.empty())
1116         getline(is, str);
1117 }
1118
1119
1120 void LyXTabular::Read(Buffer const * buf, LyXLex & lex)
1121 {
1122     string line;
1123     istream & is = lex.getStream();
1124
1125     l_getline(is, line);
1126     if (!prefixIs(line, "<LyXTabular ")) {
1127         OldFormatRead(lex, line);
1128         return;
1129     }
1130
1131     int version;
1132     int rows_arg;
1133     int columns_arg;
1134     if (!getTokenValue(line, "version", version))
1135         return;
1136     if (!getTokenValue(line, "rows", rows_arg))
1137         return;
1138     if (!getTokenValue(line, "columns", columns_arg))
1139         return;
1140     Init(rows_arg, columns_arg);
1141     l_getline(is, line);
1142     if (!prefixIs(line, "<Features ")) {
1143         lyxerr << "Wrong tabular format (expected <Feture ...> got" <<
1144             line << ")" << endl;
1145         return;
1146     }
1147     getTokenValue(line, "islongtable", is_long_tabular);
1148     getTokenValue(line, "endhead", endhead);
1149     getTokenValue(line, "endfirsthead", endfirsthead);
1150     getTokenValue(line, "endfoot", endfoot);
1151     getTokenValue(line, "endlastfoot", endlastfoot);
1152     int i, j;
1153     for(i = 0; i < rows_; ++i) {
1154         l_getline(is, line);
1155         if (!prefixIs(line, "<Row ")) {
1156             lyxerr << "Wrong tabular format (expected <Row ...> got" <<
1157                 line << ")" << endl;
1158             return;
1159         }
1160         getTokenValue(line, "topline", row_info[i].top_line);
1161         getTokenValue(line, "bottomline", row_info[i].bottom_line);
1162         getTokenValue(line, "newpage", row_info[i].newpage);
1163         for (j = 0; j < columns_; ++j) {
1164             l_getline(is,line);
1165             if (!prefixIs(line,"<Column")) {
1166                 lyxerr << "Wrong tabular format (expected <Column ...> got" <<
1167                     line << ")" << endl;
1168                 return;
1169             }
1170             if (!i) {
1171                 getTokenValue(line, "alignment", column_info[j].alignment);
1172                 getTokenValue(line, "valignment", column_info[j].valignment);
1173                 getTokenValue(line, "leftline", column_info[j].left_line);
1174                 getTokenValue(line, "rightline", column_info[j].right_line);
1175                 getTokenValue(line, "width", column_info[j].p_width);
1176                 getTokenValue(line, "special", column_info[j].align_special);
1177             }
1178             l_getline(is, line);
1179             if (!prefixIs(line, "<Cell")) {
1180                 lyxerr << "Wrong tabular format (expected <Cell ...> got" <<
1181                     line << ")" << endl;
1182                 return;
1183             }
1184             getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn);
1185             getTokenValue(line, "alignment", cell_info[i][j].alignment);
1186             getTokenValue(line, "valignment", cell_info[i][j].valignment);
1187             getTokenValue(line, "topline", cell_info[i][j].top_line);
1188             getTokenValue(line, "bottomline", cell_info[i][j].bottom_line);
1189             getTokenValue(line, "leftline", cell_info[i][j].left_line);
1190             getTokenValue(line, "rightline", cell_info[i][j].right_line);
1191             getTokenValue(line, "rotate", cell_info[i][j].rotate);
1192             getTokenValue(line, "usebox", cell_info[i][j].usebox);
1193             getTokenValue(line, "width", cell_info[i][j].p_width);
1194             getTokenValue(line, "special", cell_info[i][j].align_special);
1195             l_getline(is, line);
1196             if (prefixIs(line, "\\begin_inset")) {
1197                 cell_info[i][j].inset.Read(buf, lex);
1198                 l_getline(is, line);
1199             }
1200             if (line != "</Cell>") {
1201                 lyxerr << "Wrong tabular format (expected </Cell> got" <<
1202                     line << ")" << endl;
1203                 return;
1204             }
1205             l_getline(is, line);
1206             if (line != "</Column>") {
1207                 lyxerr << "Wrong tabular format (expected </Column> got" <<
1208                     line << ")" << endl;
1209                 return;
1210             }
1211         }
1212         l_getline(is, line);
1213         if (line != "</Row>") {
1214             lyxerr << "Wrong tabular format (expected </Row> got" <<
1215                 line << ")" << endl;
1216             return;
1217         }
1218     }
1219     while (line != "</LyXTabular>") {
1220         l_getline(is, line);
1221     }
1222     set_row_column_number_info();
1223 }
1224
1225
1226 void LyXTabular::OldFormatRead(LyXLex & lex, string const & fl)
1227 {
1228     int version;
1229     int i, j;
1230     int rows_arg = 0;
1231     int columns_arg = 0;
1232     int is_long_tabular_arg = false;
1233     int rotate_arg = false;
1234     int a = -1;
1235     int b = -1;
1236     int c = -1;
1237     int d = -1;
1238     int e = 0;
1239     int f = 0;
1240     int g = 0;
1241     int h = 0;
1242         
1243     istream & is = lex.getStream();
1244     string s(fl);
1245     if (s.length() > 8)
1246         version = lyx::atoi(s.substr(8, string::npos));
1247     else
1248         version = 1;
1249
1250     vector<int> cont_row_info;
1251
1252     if (version < 5) {
1253         lyxerr << "Tabular format < 5 is not supported anymore\n"
1254             "Get an older version of LyX (< 1.1.x) for conversion!"
1255                << endl;
1256         WriteAlert(_("Warning:"),
1257                    _("Tabular format < 5 is not supported anymore\n"),
1258                    _("Get an older version of LyX (< 1.1.x) for conversion!"));
1259         if (version > 2) {
1260             is >> rows_arg >> columns_arg >> is_long_tabular_arg
1261                >> rotate_arg >> a >> b >> c >> d;
1262         } else
1263             is >> rows_arg >> columns_arg;
1264         Init(rows_arg, columns_arg);
1265         cont_row_info = vector<int>(rows_arg);
1266         SetLongTabular(is_long_tabular_arg);
1267         SetRotateTabular(rotate_arg);
1268         string tmp;
1269         for (i = 0; i < rows_; ++i) {
1270             getline(is, tmp);
1271             cont_row_info[i] = false;
1272         }
1273         for (i = 0; i < columns_; ++i) {
1274             getline(is, tmp);
1275         }
1276         for (i = 0; i < rows_; ++i) {
1277             for (j = 0; j < columns_; ++j) {
1278                 getline(is, tmp);
1279             }
1280         }
1281     } else {
1282         is >> rows_arg >> columns_arg >> is_long_tabular_arg
1283            >> rotate_arg >> a >> b >> c >> d;
1284         Init(rows_arg, columns_arg);
1285         cont_row_info = vector<int>(rows_arg);
1286         SetLongTabular(is_long_tabular_arg);
1287         SetRotateTabular(rotate_arg);
1288         endhead = a;
1289         endfirsthead = b;
1290         endfoot = c;
1291         endlastfoot = d;
1292         for (i = 0; i < rows_; ++i) {
1293             a = b = c = d = e = f = g = h = 0;
1294             is >> a >> b >> c >> d;
1295             row_info[i].top_line = a;
1296             row_info[i].bottom_line = b;
1297             cont_row_info[i] = c;
1298             row_info[i].newpage = d;
1299         }
1300         for (i = 0; i < columns_; ++i) {
1301             string s1;
1302             string s2;
1303             is >> a >> b >> c;
1304 #if 1
1305             char ch; // skip '"'
1306             is >> ch;
1307 #else
1308             // ignore is buggy but we will use it later (Lgb)
1309             is.ignore(); // skip '"'
1310 #endif    
1311             getline(is, s1, '"');
1312 #if 1
1313             is >> ch; // skip '"'
1314 #else
1315             // ignore is buggy but we will use it later (Lgb)
1316             is.ignore(); // skip '"'
1317 #endif
1318             getline(is, s2, '"');
1319             column_info[i].alignment = static_cast<LyXAlignment>(a);
1320             column_info[i].left_line = b;
1321             column_info[i].right_line = c;
1322             column_info[i].p_width = s1;
1323             column_info[i].align_special = s2;
1324         }
1325         for (i = 0; i < rows_; ++i) {
1326             for (j = 0; j < columns_; ++j) {
1327                 string s1;
1328                 string s2;
1329                 is >> a >> b >> c >> d >> e >> f >> g;
1330 #if 1
1331                 char ch;
1332                 is >> ch; // skip '"'
1333 #else
1334                 // ignore is buggy but we will use it later (Lgb)
1335                 is.ignore(); // skip '"'
1336 #endif
1337                 getline(is, s1, '"');
1338 #if 1
1339                 is >> ch; // skip '"'
1340 #else
1341                 // ignore is buggy but we will use it later (Lgb)
1342                 is.ignore(); // skip '"'
1343 #endif
1344                 getline(is, s2, '"');
1345                 cell_info[i][j].multicolumn = static_cast<char>(a);
1346                 cell_info[i][j].alignment = static_cast<LyXAlignment>(b);
1347                 cell_info[i][j].top_line = static_cast<char>(c);
1348                 cell_info[i][j].bottom_line = static_cast<char>(d);
1349                 cell_info[i][j].left_line = column_info[j].left_line;
1350                 cell_info[i][j].right_line = column_info[j].right_line;
1351                 cell_info[i][j].rotate = static_cast<bool>(f);
1352                 cell_info[i][j].usebox = static_cast<BoxType>(g);
1353                 cell_info[i][j].align_special = s1;
1354                 cell_info[i][j].p_width = s2;
1355             }
1356         }
1357     }
1358     set_row_column_number_info(true);
1359
1360     LyXParagraph * par = new LyXParagraph;
1361     LyXParagraph * return_par = 0;
1362 #ifndef NEW_INSETS
1363     LyXParagraph::footnote_flag footnoteflag = LyXParagraph::NO_FOOTNOTE;
1364     LyXParagraph::footnote_kind footnotekind = LyXParagraph::FOOTNOTE;
1365 #endif
1366     string tmptok;
1367     int pos = 0;
1368     char depth = 0;
1369     LyXFont font(LyXFont::ALL_SANE);
1370
1371     while (lex.IsOK()) {
1372         lex.nextToken();
1373         string const token = lex.GetString();
1374         if (token.empty())
1375             continue;
1376         if (token == "\\layout"
1377             || token == "\\end_float"
1378             || token == "\\end_deeper") {
1379             lex.pushToken(token);
1380             break;
1381         }
1382         if (owner_->BufferOwner()->parseSingleLyXformat2Token(lex, par,
1383                                                               return_par,
1384                                                               token, pos,
1385                                                               depth, font
1386 #ifndef NEW_INSETS
1387                                                               ,
1388                                                               footnoteflag,
1389                                                               footnotekind
1390 #endif
1391                 ))
1392         {
1393             // the_end read
1394             lex.pushToken(token);
1395             break;
1396         }
1397         if (return_par) {
1398             lex.printError("New Paragraph allocated! This should not happen!");
1399             lex.pushToken(token);
1400             delete par;
1401             par = return_par;
1402             break;
1403         }
1404     }
1405     // now we have the par we should fill the insets with this!
1406     int cell = 0;
1407     InsetText * inset = GetCellInset(cell);
1408     int row;
1409
1410     for(int i = 0; i < par->Last(); ++i) {
1411         if (par->IsNewline(i)) {
1412             ++cell;
1413             if (cell > GetNumberOfCells()) {
1414                 lyxerr << "Some error in reading old table format occured!" <<
1415                     endl << "Terminating when reading cell[" << cell << "]!" <<
1416                     endl;
1417                 delete par;
1418                 return;
1419             }
1420             row = row_of_cell(cell);
1421             if (cont_row_info[row]) {
1422                 DeleteRow(row);
1423                 cont_row_info.erase(cont_row_info.begin() + row); //&cont_row_info[row]);
1424                 while(!IsFirstCellInRow(--cell));
1425             } else {
1426                 inset = GetCellInset(cell);
1427                 continue;
1428             }
1429             inset = GetCellInset(cell);
1430             row = row_of_cell(cell);
1431             if (!cell_info[row_of_cell(cell)][column_of_cell(cell)].usebox)
1432             {
1433                 // insert a space instead
1434                 par->Erase(i);
1435                 par->InsertChar(i, ' ');
1436             }
1437         }
1438         par->CopyIntoMinibuffer(owner_->BufferOwner()->params, i);
1439         inset->par->InsertFromMinibuffer(inset->par->Last());
1440     }
1441     delete par;
1442     Reinit();
1443 }
1444
1445
1446 string const LyXTabular::GetDocBookAlign(int cell, bool isColumn) const
1447 {
1448     int const i = isColumn ? cell : column_of_cell(cell);
1449         
1450     if (!isColumn && IsMultiColumn(cell)) {
1451        if (!cellinfo_of_cell(cell)->align_special.empty()) {
1452            return cellinfo_of_cell(cell)->align_special;
1453        } else {
1454            switch (GetAlignment(cell)) {
1455            case LYX_ALIGN_LEFT:
1456                return "left";
1457            case LYX_ALIGN_RIGHT:
1458                return "right";
1459            default:
1460                return "center";
1461            }
1462        }
1463     } else {
1464        if (!column_info[i].align_special.empty()) {
1465            return column_info[i].align_special;
1466        }
1467 #ifdef IGNORE_THIS_FOR_NOW
1468        else if (!column_info[i].p_width.empty()) {
1469            file += "p{";
1470            file += column_info[i].p_width;
1471            file += '}';
1472        }
1473 #endif
1474        else {
1475            switch (column_info[i].alignment) {
1476            case LYX_ALIGN_LEFT:
1477                return "left";
1478            case LYX_ALIGN_RIGHT:
1479                return "right";
1480            default:
1481                return "center";
1482            }
1483        }
1484     }
1485 }
1486
1487
1488 // cell <0 will tex the preamble
1489 // returns the number of printed newlines
1490 int LyXTabular::DocBookEndOfCell(ostream & os, int cell, int & depth) const
1491 {
1492     int ret = 0;
1493     if (IsLastCell(cell)) {
1494             os << newlineAndDepth(--depth)
1495                << "</ENTRY>"
1496                << newlineAndDepth(--depth)
1497                << "</ROW>"
1498                << newlineAndDepth(--depth)
1499                << "</TBODY>"
1500                << newlineAndDepth(--depth);
1501         if (is_long_tabular)
1502                 os << "</TGROUP>";
1503         else
1504                 os << "</TGROUP>"
1505                    << newlineAndDepth(--depth);
1506         ret += 4;
1507     } else {
1508         if (cell < 0) {
1509             // preamble
1510             if (is_long_tabular)
1511                     os << "<TGROUP ";
1512             else
1513                     os << "<TGROUP ";
1514             os << "COLS='"
1515                << columns_
1516                << "' COLSEP='1' ROWSEP='1'>"
1517                << newlineAndDepth(++depth);
1518             ++ret;
1519             for (int i = 0; i < columns_; ++i) {
1520                     os << "<COLSPEC ALIGN='"
1521                        << GetDocBookAlign(i, true)
1522                        << "' COLNAME='col"
1523                        << i + 1
1524                        << "' COLNUM='"
1525                        << i + 1
1526                        << "' COLSEP='";
1527                if (i == (columns_-1)) {
1528                        os << '1';
1529                } else {
1530                    if (column_info[i].right_line ||
1531                        column_info[i+1].left_line)
1532                            os << '1';
1533                    else
1534                            os << '0';
1535                }
1536                os << "'>"
1537                   << newlineAndDepth(depth);
1538                 ++ret;
1539 #ifdef NOT_HANDLED_YET_AS_I_DONT_KNOW_HOW
1540                 if (column_info[i].left_line)
1541                         os << '|';
1542 #endif
1543             }
1544             os << "<TBODY>"
1545                << newlineAndDepth(++depth)
1546                << "<ROW>"
1547                << newlineAndDepth(++depth)
1548                << "<ENTRY ALIGN='"
1549                << GetDocBookAlign(0)
1550                << "'";
1551            if (IsMultiColumn(0)) {
1552                    os << " NAMEST='col1' NAMEEND='col"
1553                       << cells_in_multicolumn(0)
1554                       << "'";
1555            }
1556            os << ">"
1557               << newlineAndDepth(++depth);
1558             ret += 3;
1559         } else {
1560             if (IsLastCellInRow(cell)) {
1561                     os << newlineAndDepth(--depth)
1562                        << "</ENTRY>"
1563                        << newlineAndDepth(--depth)
1564                        << "</ROW>"
1565                        << newlineAndDepth(depth)
1566                        << "<ROW>"
1567                        << newlineAndDepth(++depth)
1568                        << "<ENTRY ALIGN='"
1569                        << GetDocBookAlign(cell + 1)
1570                        << "' VALIGN='middle'";
1571                if (IsMultiColumn(cell + 1)) {
1572                        os << " NAMEST='col"
1573                           << column_of_cell(cell + 1) + 1
1574                           << "' NAMEEND='col"
1575                           << column_of_cell(cell + 1) +
1576                                cells_in_multicolumn(cell + 1)
1577                           << "'";
1578                }
1579                os << ">"
1580                   << newlineAndDepth(++depth);
1581                 ret += 4;
1582             } else {
1583                     os << newlineAndDepth(--depth)
1584                        << "</ENTRY>"
1585                        << newlineAndDepth(depth)
1586                        << "<ENTRY ALIGN='"
1587                        << GetDocBookAlign(cell + 1)
1588                        << "' VALIGN='middle'";
1589                if (IsMultiColumn(cell + 1)) {
1590                        os << " NAMEST='col"
1591                           << column_of_cell(cell + 1) + 1
1592                           << "' NAMEEND='col"
1593                           << column_of_cell(cell + 1) +
1594                                cells_in_multicolumn(cell + 1)
1595                           << "'";
1596                }
1597                os << ">"
1598                   << newlineAndDepth(++depth);
1599                 ret += 3;
1600             }
1601         }
1602     }
1603     return ret;
1604 }
1605
1606
1607 bool LyXTabular::IsMultiColumn(int cell, bool real) const
1608 {
1609     return ((!real || (column_of_cell(cell) != right_column_of_cell(cell))) &&
1610         (cellinfo_of_cell(cell)->multicolumn != LyXTabular::CELL_NORMAL));
1611 }
1612
1613
1614 LyXTabular::cellstruct * LyXTabular::cellinfo_of_cell(int cell) const
1615 {
1616     int const row = row_of_cell(cell);
1617     int const column = column_of_cell(cell);
1618     return  &cell_info[row][column];
1619 }
1620
1621
1622 void LyXTabular::SetMultiColumn(int cell, int number)
1623 {
1624     cellinfo_of_cell(cell)->multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
1625     cellinfo_of_cell(cell)->alignment = column_info[column_of_cell(cell)].alignment;
1626     cellinfo_of_cell(cell)->top_line = row_info[row_of_cell(cell)].top_line;
1627     cellinfo_of_cell(cell)->bottom_line = row_info[row_of_cell(cell)].bottom_line;
1628     for (number--; number > 0; --number) {
1629         cellinfo_of_cell(cell+number)->multicolumn = CELL_PART_OF_MULTICOLUMN;
1630     }
1631     set_row_column_number_info();
1632 }
1633
1634
1635 int LyXTabular::cells_in_multicolumn(int cell) const
1636 {
1637     int const row = row_of_cell(cell);
1638     int column = column_of_cell(cell);
1639     int result = 1;
1640     ++column;
1641     while ((column < columns_) &&
1642            cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN)
1643     {
1644         ++result;
1645         ++column;
1646     }
1647     return result;
1648 }
1649
1650
1651 int LyXTabular::UnsetMultiColumn(int cell)
1652 {
1653     int const row = row_of_cell(cell);
1654     int column = column_of_cell(cell);
1655     
1656     int result = 0;
1657     
1658     if (cell_info[row][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) {
1659         cell_info[row][column].multicolumn = CELL_NORMAL;
1660         ++column;
1661         while ((column < columns_) &&
1662                (cell_info[row][column].multicolumn ==CELL_PART_OF_MULTICOLUMN))
1663         {
1664             cell_info[row][column].multicolumn = CELL_NORMAL;
1665             ++column;
1666             ++result;
1667         }
1668     }
1669     set_row_column_number_info();
1670     return result;
1671 }
1672
1673
1674 void LyXTabular::SetLongTabular(bool what)
1675 {
1676     is_long_tabular = what;
1677 }
1678
1679
1680 bool LyXTabular::IsLongTabular() const
1681 {
1682     return is_long_tabular;
1683 }
1684
1685
1686 void LyXTabular::SetRotateTabular(bool flag)
1687 {
1688     rotate = flag;
1689 }
1690
1691
1692 bool LyXTabular::GetRotateTabular() const
1693 {
1694     return rotate;
1695 }
1696
1697
1698 void LyXTabular::SetRotateCell(int cell, bool flag)
1699 {
1700     cellinfo_of_cell(cell)->rotate = flag;
1701 }
1702
1703
1704 bool LyXTabular::GetRotateCell(int cell) const
1705 {
1706     return cellinfo_of_cell(cell)->rotate;
1707 }
1708
1709
1710 bool LyXTabular::NeedRotating() const
1711 {
1712     if (rotate)
1713         return true;
1714     for (int i = 0; i < rows_; ++i) {
1715         for (int j = 0; j < columns_; ++j) {
1716             if (cell_info[i][j].rotate)
1717                 return true;
1718         }
1719     }
1720     return false;
1721 }
1722
1723
1724 bool LyXTabular::IsLastCell(int cell) const
1725 {
1726     if ((cell + 1) < GetNumberOfCells())
1727         return false;
1728     return true;
1729 }
1730
1731
1732 int LyXTabular::GetCellAbove(int cell) const
1733 {
1734     if (row_of_cell(cell) > 0)
1735         return cell_info[row_of_cell(cell)-1][column_of_cell(cell)].cellno;
1736     return cell;
1737 }
1738
1739
1740 int LyXTabular::GetCellBelow(int cell) const
1741 {
1742     if (row_of_cell(cell) + 1 < rows_)
1743         return cell_info[row_of_cell(cell)+1][column_of_cell(cell)].cellno;
1744     return cell;
1745 }
1746
1747
1748 int LyXTabular::GetLastCellAbove(int cell) const
1749 {
1750     if (row_of_cell(cell) <= 0)
1751         return cell;
1752     if (!IsMultiColumn(cell))
1753         return GetCellAbove(cell);
1754     return cell_info[row_of_cell(cell) - 1][right_column_of_cell(cell)].cellno;
1755 }
1756
1757
1758 int LyXTabular::GetLastCellBelow(int cell) const
1759 {
1760     if (row_of_cell(cell) + 1 >= rows_)
1761         return cell;
1762     if (!IsMultiColumn(cell))
1763         return GetCellBelow(cell);
1764     return cell_info[row_of_cell(cell) + 1][right_column_of_cell(cell)].cellno;
1765 }
1766
1767
1768 int LyXTabular::GetCellNumber(int row, int column) const
1769 {
1770     if (column >= columns_)
1771         column = columns_ - 1;
1772     else if (column < 0)
1773         column = 0;
1774     if (row >= rows_)
1775         row = rows_ - 1;
1776     else if (row < 0)
1777         row = 0;
1778     
1779     return cell_info[row][column].cellno;
1780 }
1781
1782
1783 void LyXTabular::SetUsebox(int cell, BoxType type)
1784 {
1785     cellinfo_of_cell(cell)->usebox = type;
1786 }
1787
1788
1789 LyXTabular::BoxType LyXTabular::GetUsebox(int cell) const
1790 {
1791     if (column_info[column_of_cell(cell)].p_width.empty() &&
1792         !(IsMultiColumn(cell) && !cellinfo_of_cell(cell)->p_width.empty()))
1793         return BOX_NONE;
1794     if (cellinfo_of_cell(cell)->usebox > 1)
1795         return cellinfo_of_cell(cell)->usebox;
1796     return UseParbox(cell);
1797 }
1798
1799
1800 void LyXTabular::SetLTHead(int cell, bool first)
1801 {
1802     int const row = row_of_cell(cell);
1803     int const val = (row + 1) * (column_of_cell(cell) ? 1 : -1);
1804
1805     if (first) {
1806         if (endfirsthead == val)
1807             endfirsthead = 0;
1808         else
1809             endfirsthead = val;
1810     } else {
1811         if (endhead == val)
1812             endhead = 0;
1813         else
1814             endhead = val;
1815     }
1816 }
1817
1818
1819 bool LyXTabular::GetRowOfLTHead(int cell, int & row) const
1820 {
1821     row = endhead;
1822     if (abs(endhead) > rows_)
1823         return false;
1824     return (row_of_cell(cell) == abs(endhead) - 1);
1825 }
1826
1827
1828 bool LyXTabular::GetRowOfLTFirstHead(int cell, int & row) const
1829 {
1830     row = endfirsthead;
1831     if (abs(endfirsthead) > rows_)
1832         return false;
1833     return (row_of_cell(cell) == abs(endfirsthead) - 1);
1834 }
1835
1836
1837 void LyXTabular::SetLTFoot(int cell, bool last)
1838 {
1839     int const row = row_of_cell(cell);
1840     int const val = (row + 1) * (column_of_cell(cell) ? 1 : -1);
1841
1842     if (last) {
1843         if (endlastfoot == val)
1844             endlastfoot = 0;
1845         else
1846             endlastfoot = val;
1847     } else {
1848         if (endfoot == val)
1849             endfoot = 0;
1850         else
1851             endfoot = val;
1852     }
1853 }
1854
1855
1856 bool LyXTabular::GetRowOfLTFoot(int cell, int & row) const
1857 {
1858     row = endfoot;
1859     if ((endfoot + 1) > rows_)
1860         return false;
1861     return (row_of_cell(cell) == abs(endfoot) - 1);
1862 }
1863
1864
1865 bool LyXTabular::GetRowOfLTLastFoot(int cell, int & row) const
1866 {
1867     row = endlastfoot;
1868     if (abs(endlastfoot) > rows_)
1869         return false;
1870     return (row_of_cell(cell) == (abs(endlastfoot)-1));
1871 }
1872
1873
1874 void LyXTabular::SetLTNewPage(int cell, bool what)
1875 {
1876     row_info[row_of_cell(cell)].newpage = what;
1877 }
1878
1879
1880 bool LyXTabular::GetLTNewPage(int cell) const
1881 {
1882     return row_info[row_of_cell(cell)].newpage;
1883 }
1884
1885
1886 bool LyXTabular::SetAscentOfRow(int row, int height)
1887 {
1888     if ((row >= rows_) || (row_info[row].ascent_of_row == height))
1889         return false;
1890     row_info[row].ascent_of_row = height;
1891     return true;
1892 }
1893
1894
1895 bool LyXTabular::SetDescentOfRow(int row, int height)
1896 {
1897     if ((row >= rows_) || (row_info[row].descent_of_row == height))
1898         return false;
1899     row_info[row].descent_of_row = height;
1900     return true;
1901 }
1902
1903
1904 int LyXTabular::GetAscentOfRow(int row) const
1905 {
1906     if (row >= rows_)
1907         return 0;
1908     return row_info[row].ascent_of_row;
1909 }
1910
1911
1912 int LyXTabular::GetDescentOfRow(int row) const
1913 {
1914     if (row >= rows_)
1915         return 0;
1916     return row_info[row].descent_of_row;
1917 }
1918
1919
1920 int LyXTabular::GetHeightOfTabular() const
1921 {
1922     int height = 0;
1923
1924     for(int row = 0; row < rows_; ++row)
1925         height += GetAscentOfRow(row) + GetDescentOfRow(row) +
1926             GetAdditionalHeight(GetCellNumber(row, 0));
1927     return height;
1928 }
1929
1930
1931 bool LyXTabular::IsPartOfMultiColumn(int row, int column) const
1932 {
1933     if ((row >= rows_) || (column >= columns_))
1934         return false;
1935     return (cell_info[row][column].multicolumn==CELL_PART_OF_MULTICOLUMN);
1936 }
1937
1938
1939 int LyXTabular::TeXTopHLine(ostream & os, int row) const
1940 {
1941     int const fcell = GetFirstCellInRow(row);
1942     int const n = NumberOfCellsInRow(fcell) + fcell;
1943     int tmp = 0;
1944
1945     for (int i = fcell; i < n; ++i) {
1946         if (TopLine(i))
1947             ++tmp;
1948     }
1949     if (tmp == (n - fcell)){
1950         os << "\\hline ";
1951     } else if (tmp) {
1952         for (int i = fcell; i < n; ++i) {
1953             if (TopLine(i)) {
1954                 os << "\\cline{"
1955                    << column_of_cell(i) + 1
1956                    << '-'
1957                    << right_column_of_cell(i) + 1
1958                    << "} ";
1959             }
1960         }
1961     } else {
1962         return 0;
1963     }
1964     os << endl;
1965     return 1;
1966 }
1967
1968
1969 int LyXTabular::TeXBottomHLine(ostream & os, int row) const
1970 {
1971     int const fcell = GetFirstCellInRow(row);
1972     int const n = NumberOfCellsInRow(fcell) + fcell;
1973     int tmp = 0;
1974
1975     for (int i = fcell; i < n; ++i) {
1976         if (BottomLine(i))
1977             ++tmp;
1978     }
1979     if (tmp == (n-fcell)){
1980         os << "\\hline";
1981     } else if (tmp) {
1982         for (int i = fcell; i < n; ++i) {
1983             if (BottomLine(i)) {
1984                 os << "\\cline{"
1985                    << column_of_cell(i) + 1
1986                    << '-'
1987                    << right_column_of_cell(i) + 1
1988                    << "} ";
1989             }
1990         }
1991     } else {
1992         return 0;
1993     }
1994     os << endl;
1995     return 1;
1996 }
1997
1998
1999 int LyXTabular::TeXCellPreamble(ostream & os, int cell) const
2000 {
2001     int ret = 0;
2002
2003     if (GetRotateCell(cell)) {
2004         os << "\\begin{sideways}" << endl;
2005         ++ret;
2006     }
2007     if (IsMultiColumn(cell)) {
2008         os << "\\multicolumn{" << cells_in_multicolumn(cell) << "}{";
2009         if (!cellinfo_of_cell(cell+1)->align_special.empty()) {
2010             os << cellinfo_of_cell(cell+1)->align_special << "}{";
2011         } else {
2012             if (LeftLine(cell))
2013                 os << '|';
2014             if (!GetPWidth(cell).empty()) {
2015                 switch(GetVAlignment(cell)) {
2016                 case LYX_VALIGN_TOP:
2017                     os << "p";
2018                     break;
2019                 case LYX_VALIGN_CENTER:
2020                     os << "m";
2021                     break;
2022                 case LYX_VALIGN_BOTTOM:
2023                     os << "b";
2024                     break;
2025                 }
2026                 os << "{" << GetPWidth(cell) << '}';
2027             } else {
2028                 switch (GetAlignment(cell)) {
2029                 case LYX_ALIGN_LEFT:
2030                     os << 'l';
2031                     break;
2032                 case LYX_ALIGN_RIGHT:
2033                     os << 'r';
2034                     break;
2035                 default:
2036                     os << 'c';
2037                     break;
2038                 }
2039             }
2040             if (RightLine(cell))
2041                 os << '|';
2042             if (((cell + 1) < numberofcells) && !IsFirstCellInRow(cell+1) &&
2043                 LeftLine(cell+1))
2044                 os << '|';
2045             os << "}{";
2046         }
2047     }
2048     if (GetUsebox(cell) == BOX_PARBOX) {
2049         os << "\\parbox[";
2050         switch(GetVAlignment(cell)) {
2051         case LYX_VALIGN_TOP:
2052             os << "t";
2053             break;
2054         case LYX_VALIGN_CENTER:
2055             os << "c";
2056             break;
2057         case LYX_VALIGN_BOTTOM:
2058             os << "b";
2059             break;
2060         }
2061         os << "]{" << GetPWidth(cell) << "}{";
2062     } else if (GetUsebox(cell) == BOX_MINIPAGE) {
2063         os << "\\begin{minipage}[";
2064         switch(GetVAlignment(cell)) {
2065         case LYX_VALIGN_TOP:
2066             os << "t";
2067             break;
2068         case LYX_VALIGN_CENTER:
2069             os << "m";
2070             break;
2071         case LYX_VALIGN_BOTTOM:
2072             os << "b";
2073             break;
2074         }
2075         os << "]{" << GetPWidth(cell) << "}\n";
2076         ++ret;
2077     }
2078     return ret;
2079 }
2080
2081
2082 int LyXTabular::TeXCellPostamble(ostream & os, int cell) const
2083 {
2084     int ret = 0;
2085
2086     // usual cells
2087     if (GetUsebox(cell) == BOX_PARBOX)
2088         os << "}";
2089     else if (GetUsebox(cell) == BOX_MINIPAGE) {
2090         os << "%\n\\end{minipage}";
2091         ret += 2;
2092     }
2093     if (IsMultiColumn(cell)){
2094         os << '}';
2095     }
2096     if (GetRotateCell(cell)) {
2097         os << "%\n\\end{sideways}";
2098         ++ret;
2099     }
2100     return ret;
2101 }
2102
2103
2104 int LyXTabular::Latex(Buffer const * buf,
2105                       ostream & os, bool fragile, bool fp) const
2106 {
2107     int ret = 0;
2108     int cell = 0;
2109
2110     //+---------------------------------------------------------------------
2111     //+                      first the opening preamble                    +
2112     //+---------------------------------------------------------------------
2113
2114     if (rotate) {
2115         os << "\\begin{sideways}" << endl;
2116         ++ret;
2117     }
2118     if (is_long_tabular)
2119         os << "\\begin{longtable}{";
2120     else
2121         os << "\\begin{tabular}{";
2122     for (int i = 0; i < columns_; ++i) {
2123         if (column_info[i].left_line)
2124             os << '|';
2125         if (!column_info[i].align_special.empty()) {
2126             os << column_info[i].align_special;
2127         } else if (!column_info[i].p_width.empty()) {
2128             switch(column_info[i].valignment) {
2129             case LYX_VALIGN_TOP:
2130                 os << "p";
2131                 break;
2132             case LYX_VALIGN_CENTER:
2133                 os << "m";
2134                 break;
2135             case LYX_VALIGN_BOTTOM:
2136                 os << "b";
2137                 break;
2138             }
2139             os << "{"
2140                << column_info[i].p_width
2141                << '}';
2142         } else {
2143             switch (column_info[i].alignment) {
2144             case LYX_ALIGN_LEFT:
2145                 os << 'l';
2146                 break;
2147             case LYX_ALIGN_RIGHT:
2148                 os << 'r';
2149                 break;
2150             default:
2151                 os << 'c';
2152                 break;
2153             }
2154         }
2155         if (column_info[i].right_line)
2156             os << '|';
2157     }
2158     os << "}" << endl;
2159     ++ret;
2160
2161     //+---------------------------------------------------------------------
2162     //+                      the single row and columns (cells)            +
2163     //+---------------------------------------------------------------------
2164
2165     int bret;
2166     for(int i = 0; i < rows_; ++i) {
2167         ret += TeXTopHLine(os, i);
2168         bret = ret;
2169         if (IsLongTabular()) {
2170             if ((endhead < 0) && (i == (abs(endhead)-1))) {
2171                 os << "\\endhead\n";
2172                 ++ret;
2173             }
2174             if ((endfirsthead < 0) && (i == (abs(endfirsthead)-1))) {
2175                 os << "\\endfirsthead\n";
2176                 ++ret;
2177             }
2178             if ((endfoot < 0) && (i == (abs(endfoot)-1))) {
2179                 os << "\\endfoot\n";
2180                 ++ret;
2181             }
2182             if ((endlastfoot < 0) && (i == (abs(endlastfoot)-1))) {
2183                 os << "\\endlastfoot\n";
2184                 ++ret;
2185             }
2186         }
2187         if (ret > bret) {
2188             if (i > 0)
2189                 ret += TeXBottomHLine(os, i-1);
2190             ret += TeXTopHLine(os, i);
2191         }
2192         for(int j = 0; j < columns_; ++j) {
2193             if (IsPartOfMultiColumn(i,j))
2194                 continue;
2195             ret += TeXCellPreamble(os, cell);
2196             ret += GetCellInset(cell)->Latex(buf, os, fragile, fp);
2197             ret += TeXCellPostamble(os, cell);
2198             if (!IsLastCellInRow(cell)) { // not last cell in row
2199                 os << "&" << endl;
2200                 ++ret;
2201             }
2202             ++cell;
2203         }
2204         os << "\\\\" << endl;
2205         ret += TeXBottomHLine(os, i);
2206         bret = ret;
2207         if (IsLongTabular()) {
2208             if ((endhead > 0) && (i == (endhead - 1))) {
2209                 os << "\\endhead\n";
2210                 ++ret;
2211             }
2212             if ((endfirsthead > 0) && (i == (endfirsthead - 1))) {
2213                 os << "\\endfirsthead\n";
2214                 ++ret;
2215             }
2216             if ((endfoot > 0) && (i == (endfoot - 1))) {
2217                 os << "\\endfoot\n";
2218                 ++ret;
2219             }
2220             if ((endlastfoot > 0) && (i == (endlastfoot - 1))) {
2221                 os << "\\endlastfoot\n";
2222                 ++ret;
2223             }
2224             if (ret > bret)
2225                 ret += TeXBottomHLine(os, i);
2226             if (row_info[i].newpage) {
2227                 os << "\\newpage\n";
2228                 ++ret;
2229             }
2230         }
2231     }
2232
2233     //+---------------------------------------------------------------------
2234     //+                      the closing of the tabular                    +
2235     //+---------------------------------------------------------------------
2236
2237     if (is_long_tabular)
2238         os << "\\end{longtable}";
2239     else
2240         os << "\\end{tabular}";
2241     if (rotate) {
2242         os << "\n\\end{sideways}";
2243         ++ret;
2244     }
2245
2246     return ret;
2247 }
2248
2249
2250 int LyXTabular::DocBook(Buffer const * buf, ostream & os) const
2251 {
2252     int ret = 0;
2253     int cell = 0;
2254
2255     //+---------------------------------------------------------------------
2256     //+                      first the opening preamble                    +
2257     //+---------------------------------------------------------------------
2258
2259     os << "<tgroup cols=\"" << columns_
2260        << "\" colsep=\"1\" rowsep=\"1\">" << endl;
2261     
2262     for (int i = 0; i < columns_; ++i) {
2263         os << "<colspec colname=\"col" << i << "\" align=\"";
2264         switch (column_info[i].alignment) {
2265         case LYX_ALIGN_LEFT:
2266             os << "left";
2267             break;
2268         case LYX_ALIGN_RIGHT:
2269             os << "right";
2270             break;
2271         default:
2272             os << "center";
2273             break;
2274         }
2275         os << "\"/>" << endl;
2276         ++ret;
2277     }
2278
2279     //+---------------------------------------------------------------------
2280     //+                      the single row and columns (cells)            +
2281     //+---------------------------------------------------------------------
2282
2283     os << "<tbody>" << endl;
2284     for(int i = 0; i < rows_; ++i) {
2285         os << "<row>" << endl;
2286         for(int j = 0; j < columns_; ++j) {
2287             if (IsPartOfMultiColumn(i,j))
2288                 continue;
2289             
2290             os << "<entry align=\"";
2291             switch(GetAlignment(cell)) {
2292             case LYX_ALIGN_LEFT:
2293                 os << "left";
2294                 break;
2295             case LYX_ALIGN_RIGHT:
2296                 os << "right";
2297                 break;
2298             default:
2299                 os << "center";
2300                 break;
2301             }
2302             
2303             os << "\" valign=\"";
2304             switch(GetVAlignment(cell)) {
2305             case LYX_VALIGN_TOP:
2306                 os << "top";
2307                 break;
2308             case LYX_VALIGN_BOTTOM:
2309                 os << "bottom";
2310                 break;
2311             case LYX_VALIGN_CENTER:
2312                 os << "middle";
2313             }
2314             os << "\"";
2315             
2316             if (IsMultiColumn(cell)) {
2317                 os << " namest=\"col" << j << "\" ";
2318                 os << "nameend=\"col" << j + cells_in_multicolumn(cell) - 1<< "\"";
2319             }
2320             
2321             os << ">";
2322             ret += GetCellInset(cell)->DocBook(buf, os);
2323             os << "</entry>";
2324             ++cell;
2325         }
2326         os << "</row>" << endl;
2327     }
2328     os << "</tbody>" << endl;
2329     //+---------------------------------------------------------------------
2330     //+                      the closing of the tabular                    +
2331     //+---------------------------------------------------------------------
2332
2333     os << "</tgroup>";
2334     ++ret;
2335
2336     return ret;
2337 }
2338
2339
2340 static void print_n_chars(ostream & os, unsigned char ch, int const n)
2341 {
2342     for(int i=0; i < n; ++i)
2343         os << ch;
2344 }
2345
2346 int LyXTabular::AsciiTopHLine(ostream & os, int row,
2347                               vector<unsigned int> const & clen) const
2348 {
2349     int const fcell = GetFirstCellInRow(row);
2350     int const n = NumberOfCellsInRow(fcell) + fcell;
2351     int len;
2352     int column = 0;
2353     unsigned char ch;
2354     int tmp = 0;
2355
2356     for (int i = fcell; i < n; ++i) {
2357         if (TopLine(i))
2358             ++tmp;
2359     }
2360     if (!tmp)
2361         return 0;
2362
2363     for (int i = fcell; i < n; ++i) {
2364         if (TopLine(i)) {
2365             if (LeftLine(i))
2366                 os << "+-";
2367             else
2368                 os << "--";
2369             ch = '-';
2370         } else {
2371             os << "  ";
2372             ch = ' ';
2373         }
2374         column = column_of_cell(i);
2375         len = clen[column];
2376         while(IsPartOfMultiColumn(row, ++column))
2377             len += clen[column] + 4;
2378         print_n_chars(os, ch, len);
2379         if (TopLine(i)) {
2380             if (RightLine(i))
2381                 os << "-+";
2382             else
2383                 os << "--";
2384         } else {
2385             os << "  ";
2386         }
2387     }
2388     os << endl;
2389     return 1;
2390 }
2391
2392
2393 int LyXTabular::AsciiBottomHLine(ostream & os, int row,
2394                                  vector<unsigned int> const & clen) const
2395 {
2396     int const fcell = GetFirstCellInRow(row);
2397     int const n = NumberOfCellsInRow(fcell) + fcell;
2398     int len;
2399     int column = 0;
2400     unsigned char ch;
2401     int tmp = 0;
2402
2403     for (int i = fcell; i < n; ++i) {
2404         if (BottomLine(i))
2405             ++tmp;
2406     }
2407     if (!tmp)
2408         return 0;
2409
2410     for (int i = fcell; i < n; ++i) {
2411         if (BottomLine(i)) {
2412             if (LeftLine(i))
2413                 os << "+-";
2414             else
2415                 os << "--";
2416             ch = '-';
2417         } else {
2418             os << "  ";
2419             ch = ' ';
2420         }
2421         column = column_of_cell(i);
2422         len = clen[column];
2423         while(IsPartOfMultiColumn(row, ++column))
2424             len += clen[column] + 4;
2425         print_n_chars(os, ch, len);
2426         if (BottomLine(i)) {
2427             if (RightLine(i))
2428                 os << "-+";
2429             else
2430                 os << "--";
2431         } else {
2432             os << "  ";
2433         }
2434     }
2435     os << endl;
2436     return 1;
2437 }
2438
2439
2440 int LyXTabular::AsciiPrintCell(Buffer const * buf, ostream & os,
2441                                int cell, int row, int column,
2442                                vector<unsigned int> const & clen) const
2443 {
2444     ostringstream sstr;
2445     int ret = GetCellInset(cell)->Ascii(buf, sstr, 0);
2446
2447     if (LeftLine(cell))
2448         os << "| ";
2449     else
2450         os << "  ";
2451
2452     unsigned int len1 = sstr.str().length();
2453     unsigned int len2 = clen[column];
2454     while(IsPartOfMultiColumn(row, ++column))
2455         len2 += clen[column] + 4;
2456     len2 -= len1;
2457
2458     switch(GetAlignment(cell)) {
2459     default:
2460     case LYX_ALIGN_LEFT:
2461         len1 = 0;
2462         break;
2463     case LYX_ALIGN_RIGHT:
2464         len1 = len2;
2465         len2 = 0;
2466         break;
2467     case LYX_ALIGN_CENTER:
2468         len1 = len2 / 2;
2469         len2 -= len1;
2470         break;
2471     }
2472
2473     for(unsigned int i = 0; i < len1; ++i)
2474         os << " ";
2475     os << sstr.str();
2476     for(unsigned int i = 0; i < len2; ++i)
2477         os << " ";
2478     if (RightLine(cell))
2479         os << " |";
2480     else
2481         os << "  ";
2482
2483     return ret * 0; // eh? (Lgb)
2484 }
2485
2486
2487 int LyXTabular::Ascii(Buffer const * buf, ostream & os) const
2488 {
2489     int ret = 0;
2490
2491     //+---------------------------------------------------------------------
2492     //+           first calculate the width of the single columns          +
2493     //+---------------------------------------------------------------------
2494     vector<unsigned int> clen(columns_);
2495
2496     // first all non (real) multicolumn cells!
2497     for(int j = 0; j < columns_; ++j) {
2498         clen[j] = 0;
2499         for(int i = 0; i < rows_; ++i) {
2500             int cell = GetCellNumber(i, j);
2501             if (IsMultiColumn(cell, true))
2502                 continue;
2503             ostringstream sstr;
2504             GetCellInset(cell)->Ascii(buf, sstr, 0);
2505             if (clen[j] < sstr.str().length())
2506                 clen[j] = sstr.str().length();
2507         }
2508     }
2509     // then all (real) multicolumn cells!
2510     for(int j = 0; j < columns_; ++j) {
2511         for(int i = 0; i < rows_; ++i) {
2512             int cell = GetCellNumber(i, j);
2513             if (!IsMultiColumn(cell, true) || IsPartOfMultiColumn(i, j))
2514                 continue;
2515             ostringstream sstr;
2516             GetCellInset(cell)->Ascii(buf, sstr, 0);
2517             int len = int(sstr.str().length());
2518             int const n = cells_in_multicolumn(cell);
2519             for (int k = j; (len > 0) && (k < (j + n - 1)); ++k)
2520                 len -= clen[k];
2521             if (len > int(clen[j + n - 1]))
2522                 clen[j + n - 1] = len;
2523         }
2524     }
2525     int cell = 0;
2526     for(int i = 0; i < rows_; ++i) {
2527         AsciiTopHLine(os, i, clen);
2528         for(int j = 0; j < columns_; ++j) {
2529             if (IsPartOfMultiColumn(i,j))
2530                 continue;
2531             ret += AsciiPrintCell(buf, os, cell, i, j, clen);
2532             ++cell;
2533         }
2534         os << endl;
2535         AsciiBottomHLine(os, i, clen);
2536     }
2537     return ret;
2538 }
2539
2540
2541 InsetText * LyXTabular::GetCellInset(int cell) const
2542 {
2543     return & cell_info[row_of_cell(cell)][column_of_cell(cell)].inset;
2544 }
2545
2546
2547 void LyXTabular::Validate(LaTeXFeatures & features) const
2548 {
2549     if (IsLongTabular())
2550         features.longtable = true;
2551     if (NeedRotating())
2552         features.rotating = true;
2553     for(int cell = 0; cell < numberofcells; ++cell) {
2554         if (GetVAlignment(cell) != LYX_VALIGN_TOP)
2555             features.array = true;
2556         GetCellInset(cell)->Validate(features);
2557     }
2558 }
2559
2560
2561 LyXTabular::BoxType LyXTabular::UseParbox(int cell) const
2562 {
2563     LyXParagraph * par = GetCellInset(cell)->par;
2564
2565     for(; par; par = par->next) {
2566         for(int i = 0; i < par->Last(); ++i) {
2567             if (par->GetChar(i) == LyXParagraph::META_NEWLINE)
2568                 return BOX_PARBOX;
2569         }
2570     }
2571     return BOX_NONE;
2572 }