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