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