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