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