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