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