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