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