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