]> git.lyx.org Git - lyx.git/blob - src/insets/InsetTabular.cpp
InsetTabular.cpp: fix #6985 - we need to break after unsetting otherwise case Tabular...
[lyx.git] / src / insets / InsetTabular.cpp
1 /**
2  * \file InsetTabular.cpp
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  * \author Uwe Stöhr
15  * \author Edwin Leuven
16  *
17  * Full author contact details are available in file CREDITS.
18  */
19
20 #include <config.h>
21
22 #include "InsetTabular.h"
23
24 #include "buffer_funcs.h"
25 #include "Buffer.h"
26 #include "BufferParams.h"
27 #include "BufferView.h"
28 #include "CoordCache.h"
29 #include "Counters.h"
30 #include "Cursor.h"
31 #include "CutAndPaste.h"
32 #include "DispatchResult.h"
33 #include "FuncRequest.h"
34 #include "FuncStatus.h"
35 #include "Language.h"
36 #include "LaTeXFeatures.h"
37 #include "Lexer.h"
38 #include "LyX.h"
39 #include "LyXRC.h"
40 #include "MetricsInfo.h"
41 #include "OutputParams.h"
42 #include "output_xhtml.h"
43 #include "Paragraph.h"
44 #include "ParagraphParameters.h"
45 #include "ParIterator.h"
46 #include "TextClass.h"
47 #include "TextMetrics.h"
48
49 #include "frontends/Application.h"
50 #include "frontends/alert.h"
51 #include "frontends/Clipboard.h"
52 #include "frontends/Painter.h"
53 #include "frontends/Selection.h"
54
55 #include "support/convert.h"
56 #include "support/debug.h"
57 #include "support/docstream.h"
58 #include "support/FileName.h"
59 #include "support/gettext.h"
60 #include "support/lassert.h"
61 #include "support/lstrings.h"
62
63 #include <boost/scoped_ptr.hpp>
64
65 #include <sstream>
66 #include <iostream>
67 #include <limits>
68 #include <cstring>
69
70 using namespace std;
71 using namespace lyx::support;
72
73
74
75 namespace lyx {
76
77 using cap::dirtyTabularStack;
78 using cap::tabularStackDirty;
79
80 using graphics::PreviewLoader;
81
82 using frontend::Painter;
83 using frontend::Clipboard;
84
85 namespace Alert = frontend::Alert;
86
87
88 namespace {
89
90 int const ADD_TO_HEIGHT = 2; // in cell
91 int const ADD_TO_TABULAR_WIDTH = 6; // horiz space before and after the table
92 int const default_line_space = 10; // ?
93 int const WIDTH_OF_LINE = 5; // space between double lines
94
95
96 ///
97 boost::scoped_ptr<Tabular> paste_tabular;
98
99
100 struct TabularFeature {
101         Tabular::Feature action;
102         string feature;
103         bool need_value;
104 };
105
106
107 TabularFeature tabularFeature[] =
108 {
109         { Tabular::APPEND_ROW, "append-row", false },
110         { Tabular::APPEND_COLUMN, "append-column", false },
111         { Tabular::DELETE_ROW, "delete-row", false },
112         { Tabular::DELETE_COLUMN, "delete-column", false },
113         { Tabular::COPY_ROW, "copy-row", false },
114         { Tabular::COPY_COLUMN, "copy-column", false },
115         { Tabular::SET_LINE_TOP, "set-line-top", true },
116         { Tabular::SET_LINE_BOTTOM, "set-line-bottom", true },
117         { Tabular::SET_LINE_LEFT, "set-line-left", true },
118         { Tabular::SET_LINE_RIGHT, "set-line-right", true },
119         //FIXME: get rid of those 4 TOGGLE actions in favor of the 4 above.
120         { Tabular::TOGGLE_LINE_TOP, "toggle-line-top", false },
121         { Tabular::TOGGLE_LINE_BOTTOM, "toggle-line-bottom", false },
122         { Tabular::TOGGLE_LINE_LEFT, "toggle-line-left", false },
123         { Tabular::TOGGLE_LINE_RIGHT, "toggle-line-right", false },
124         { Tabular::ALIGN_LEFT, "align-left", false },
125         { Tabular::ALIGN_RIGHT, "align-right", false },
126         { Tabular::ALIGN_CENTER, "align-center", false },
127         { Tabular::ALIGN_BLOCK, "align-block", false },
128         { Tabular::ALIGN_DECIMAL, "align-decimal", false },
129         { Tabular::VALIGN_TOP, "valign-top", false },
130         { Tabular::VALIGN_BOTTOM, "valign-bottom", false },
131         { Tabular::VALIGN_MIDDLE, "valign-middle", false },
132         { Tabular::M_ALIGN_LEFT, "m-align-left", false },
133         { Tabular::M_ALIGN_RIGHT, "m-align-right", false },
134         { Tabular::M_ALIGN_CENTER, "m-align-center", false },
135         { Tabular::M_VALIGN_TOP, "m-valign-top", false },
136         { Tabular::M_VALIGN_BOTTOM, "m-valign-bottom", false },
137         { Tabular::M_VALIGN_MIDDLE, "m-valign-middle", false },
138         { Tabular::MULTICOLUMN, "multicolumn", false },
139         { Tabular::SET_MULTICOLUMN, "set-multicolumn", false },
140         { Tabular::UNSET_MULTICOLUMN, "unset-multicolumn", false },
141         { Tabular::MULTIROW, "multirow", false },
142         { Tabular::SET_MULTIROW, "set-multirow", false },
143         { Tabular::UNSET_MULTIROW, "unset-multirow", false },
144         { Tabular::SET_ALL_LINES, "set-all-lines", false },
145         { Tabular::UNSET_ALL_LINES, "unset-all-lines", false },
146         { Tabular::SET_LONGTABULAR, "set-longtabular", false },
147         { Tabular::UNSET_LONGTABULAR, "unset-longtabular", false },
148         { Tabular::SET_PWIDTH, "set-pwidth", true },
149         { Tabular::SET_MPWIDTH, "set-mpwidth", true },
150         { Tabular::SET_ROTATE_TABULAR, "set-rotate-tabular", false },
151         { Tabular::UNSET_ROTATE_TABULAR, "unset-rotate-tabular", false },
152         { Tabular::TOGGLE_ROTATE_TABULAR, "toggle-rotate-tabular", false },
153         { Tabular::SET_ROTATE_CELL, "set-rotate-cell", false },
154         { Tabular::UNSET_ROTATE_CELL, "unset-rotate-cell", false },
155         { Tabular::TOGGLE_ROTATE_CELL, "toggle-rotate-cell", false },
156         { Tabular::SET_USEBOX, "set-usebox", true },
157         { Tabular::SET_LTHEAD, "set-lthead", true },
158         { Tabular::UNSET_LTHEAD, "unset-lthead", true },
159         { Tabular::SET_LTFIRSTHEAD, "set-ltfirsthead", true },
160         { Tabular::UNSET_LTFIRSTHEAD, "unset-ltfirsthead", true },
161         { Tabular::SET_LTFOOT, "set-ltfoot", true },
162         { Tabular::UNSET_LTFOOT, "unset-ltfoot", true },
163         { Tabular::SET_LTLASTFOOT, "set-ltlastfoot", true },
164         { Tabular::UNSET_LTLASTFOOT, "unset-ltlastfoot", true },
165         { Tabular::SET_LTNEWPAGE, "set-ltnewpage", false },
166         { Tabular::TOGGLE_LTCAPTION, "toggle-ltcaption", false },
167         { Tabular::SET_LTCAPTION, "set-ltcaption", false },
168         { Tabular::UNSET_LTCAPTION, "unset-ltcaption", false },
169         { Tabular::SET_SPECIAL_COLUMN, "set-special-column", true },
170         { Tabular::SET_SPECIAL_MULTICOLUMN, "set-special-multicolumn", true },
171         { Tabular::SET_BOOKTABS, "set-booktabs", false },
172         { Tabular::UNSET_BOOKTABS, "unset-booktabs", false },
173         { Tabular::SET_TOP_SPACE, "set-top-space", true },
174         { Tabular::SET_BOTTOM_SPACE, "set-bottom-space", true },
175         { Tabular::SET_INTERLINE_SPACE, "set-interline-space", true },
176         { Tabular::SET_BORDER_LINES, "set-border-lines", false },
177         { Tabular::TABULAR_VALIGN_TOP, "tabular-valign-top", false},
178         { Tabular::TABULAR_VALIGN_MIDDLE, "tabular-valign-middle", false},
179         { Tabular::TABULAR_VALIGN_BOTTOM, "tabular-valign-bottom", false},
180         { Tabular::LONGTABULAR_ALIGN_LEFT, "longtabular-align-left", false },
181         { Tabular::LONGTABULAR_ALIGN_CENTER, "longtabular-align-center", false },
182         { Tabular::LONGTABULAR_ALIGN_RIGHT, "longtabular-align-right", false },
183         { Tabular::SET_DECIMAL_POINT, "set-decimal-point", true },
184         { Tabular::LAST_ACTION, "", false }
185 };
186
187
188 template <class T>
189 string const write_attribute(string const & name, T const & t)
190 {
191         string const s = tostr(t);
192         return s.empty() ? s : " " + name + "=\"" + s + "\"";
193 }
194
195 template <>
196 string const write_attribute(string const & name, string const & t)
197 {
198         return t.empty() ? t : " " + name + "=\"" + t + "\"";
199 }
200
201
202 template <>
203 string const write_attribute(string const & name, docstring const & t)
204 {
205         return t.empty() ? string() : " " + name + "=\"" + to_utf8(t) + "\"";
206 }
207
208
209 template <>
210 string const write_attribute(string const & name, bool const & b)
211 {
212         // we write only true attribute values so we remove a bit of the
213         // file format bloat for tabulars.
214         return b ? write_attribute(name, convert<string>(b)) : string();
215 }
216
217
218 template <>
219 string const write_attribute(string const & name, int const & i)
220 {
221         // we write only true attribute values so we remove a bit of the
222         // file format bloat for tabulars.
223         return i ? write_attribute(name, convert<string>(i)) : string();
224 }
225
226
227 template <>
228 string const write_attribute(string const & name, Tabular::idx_type const & i)
229 {
230         // we write only true attribute values so we remove a bit of the
231         // file format bloat for tabulars.
232         return i ? write_attribute(name, convert<string>(i)) : string();
233 }
234
235
236 template <>
237 string const write_attribute(string const & name, Length const & value)
238 {
239         // we write only the value if we really have one same reson as above.
240         return value.zero() ? string() : write_attribute(name, value.asString());
241 }
242
243
244 string const tostr(LyXAlignment const & num)
245 {
246         switch (num) {
247         case LYX_ALIGN_NONE:
248                 return "none";
249         case LYX_ALIGN_BLOCK:
250                 return "block";
251         case LYX_ALIGN_LEFT:
252                 return "left";
253         case LYX_ALIGN_CENTER:
254                 return "center";
255         case LYX_ALIGN_RIGHT:
256                 return "right";
257         case LYX_ALIGN_LAYOUT:
258                 return "layout";
259         case LYX_ALIGN_SPECIAL:
260                 return "special";
261         case LYX_ALIGN_DECIMAL:
262                 return "decimal";
263         }
264         return string();
265 }
266
267
268 string const tostr(Tabular::HAlignment const & num)
269 {
270         switch (num) {
271         case Tabular::LYX_LONGTABULAR_ALIGN_LEFT:
272                 return "left";
273         case Tabular::LYX_LONGTABULAR_ALIGN_CENTER:
274                 return "center";
275         case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT:
276                 return "right";
277         }
278         return string();
279 }
280
281
282 string const tostr(Tabular::VAlignment const & num)
283 {
284         switch (num) {
285         case Tabular::LYX_VALIGN_TOP:
286                 return "top";
287         case Tabular::LYX_VALIGN_MIDDLE:
288                 return "middle";
289         case Tabular::LYX_VALIGN_BOTTOM:
290                 return "bottom";
291         }
292         return string();
293 }
294
295
296 string const tostr(Tabular::BoxType const & num)
297 {
298         switch (num) {
299         case Tabular::BOX_NONE:
300                 return "none";
301         case Tabular::BOX_PARBOX:
302                 return "parbox";
303         case Tabular::BOX_MINIPAGE:
304                 return "minipage";
305         }
306         return string();
307 }
308
309
310 // I would have liked a fromstr template a lot better. (Lgb)
311 bool string2type(string const str, LyXAlignment & num)
312 {
313         if (str == "none")
314                 num = LYX_ALIGN_NONE;
315         else if (str == "block")
316                 num = LYX_ALIGN_BLOCK;
317         else if (str == "left")
318                 num = LYX_ALIGN_LEFT;
319         else if (str == "center")
320                 num = LYX_ALIGN_CENTER;
321         else if (str == "right")
322                 num = LYX_ALIGN_RIGHT;
323         else if (str == "decimal")
324                 num = LYX_ALIGN_DECIMAL;
325         else
326                 return false;
327         return true;
328 }
329
330
331 bool string2type(string const str, Tabular::HAlignment & num)
332 {
333         if (str == "left")
334                 num = Tabular::LYX_LONGTABULAR_ALIGN_LEFT;
335         else if (str == "center" )
336                 num = Tabular::LYX_LONGTABULAR_ALIGN_CENTER;
337         else if (str == "right")
338                 num = Tabular::LYX_LONGTABULAR_ALIGN_RIGHT;
339         else
340                 return false;
341         return true;
342 }
343
344
345 bool string2type(string const str, Tabular::VAlignment & num)
346 {
347         if (str == "top")
348                 num = Tabular::LYX_VALIGN_TOP;
349         else if (str == "middle" )
350                 num = Tabular::LYX_VALIGN_MIDDLE;
351         else if (str == "bottom")
352                 num = Tabular::LYX_VALIGN_BOTTOM;
353         else
354                 return false;
355         return true;
356 }
357
358
359 bool string2type(string const str, Tabular::BoxType & num)
360 {
361         if (str == "none")
362                 num = Tabular::BOX_NONE;
363         else if (str == "parbox")
364                 num = Tabular::BOX_PARBOX;
365         else if (str == "minipage")
366                 num = Tabular::BOX_MINIPAGE;
367         else
368                 return false;
369         return true;
370 }
371
372
373 bool string2type(string const str, bool & num)
374 {
375         if (str == "true")
376                 num = true;
377         else if (str == "false")
378                 num = false;
379         else
380                 return false;
381         return true;
382 }
383
384
385 bool getTokenValue(string const & str, char const * token, string & ret)
386 {
387         ret.erase();
388         size_t token_length = strlen(token);
389         size_t pos = str.find(token);
390
391         if (pos == string::npos || pos + token_length + 1 >= str.length()
392                 || str[pos + token_length] != '=')
393                 return false;
394         pos += token_length + 1;
395         char ch = str[pos];
396         if (ch != '"' && ch != '\'') { // only read till next space
397                 ret += ch;
398                 ch = ' ';
399         }
400         while (pos < str.length() - 1 && str[++pos] != ch)
401                 ret += str[pos];
402
403         return true;
404 }
405
406
407 bool getTokenValue(string const & str, char const * token, docstring & ret)
408 {
409         string tmp;
410         bool const success = getTokenValue(str, token, tmp);
411         ret = from_utf8(tmp);
412         return success;
413 }
414
415
416 bool getTokenValue(string const & str, char const * token, int & num)
417 {
418         string tmp;
419         num = 0;
420         if (!getTokenValue(str, token, tmp))
421                 return false;
422         num = convert<int>(tmp);
423         return true;
424 }
425
426
427 bool getTokenValue(string const & str, char const * token, LyXAlignment & num)
428 {
429         string tmp;
430         return getTokenValue(str, token, tmp) && string2type(tmp, num);
431 }
432
433
434 bool getTokenValue(string const & str, char const * token,
435                                    Tabular::HAlignment & num)
436 {
437         string tmp;
438         return getTokenValue(str, token, tmp) && string2type(tmp, num);
439 }
440
441
442 bool getTokenValue(string const & str, char const * token,
443                                    Tabular::VAlignment & num)
444 {
445         string tmp;
446         return getTokenValue(str, token, tmp) && string2type(tmp, num);
447 }
448
449
450 bool getTokenValue(string const & str, char const * token,
451                                    Tabular::BoxType & num)
452 {
453         string tmp;
454         return getTokenValue(str, token, tmp) && string2type(tmp, num);
455 }
456
457
458 bool getTokenValue(string const & str, char const * token, bool & flag)
459 {
460         // set the flag always to false as this should be the default for bools
461         // not in the file-format.
462         flag = false;
463         string tmp;
464         return getTokenValue(str, token, tmp) && string2type(tmp, flag);
465 }
466
467
468 bool getTokenValue(string const & str, char const * token, Length & len)
469 {
470         // set the length to be zero() as default as this it should be if not
471         // in the file format.
472         len = Length();
473         string tmp;
474         return getTokenValue(str, token, tmp) && isValidLength(tmp, &len);
475 }
476
477
478 bool getTokenValue(string const & str, char const * token, Length & len, bool & flag)
479 {
480         len = Length();
481         flag = false;
482         string tmp;
483         if (!getTokenValue(str, token, tmp))
484                 return false;
485         if (tmp == "default") {
486                 flag = true;
487                 return  true;
488         }
489         return isValidLength(tmp, &len);
490 }
491
492
493 void l_getline(istream & is, string & str)
494 {
495         str.erase();
496         while (str.empty()) {
497                 getline(is, str);
498                 if (!str.empty() && str[str.length() - 1] == '\r')
499                         str.erase(str.length() - 1);
500         }
501 }
502
503 } // namespace
504
505
506 string const featureAsString(Tabular::Feature action)
507 {
508         for (size_t i = 0; i != Tabular::LAST_ACTION; ++i) {
509                 if (tabularFeature[i].action == action)
510                         return tabularFeature[i].feature;
511         }
512         return string();
513 }
514
515
516 InsetTableCell splitCell(InsetTableCell & head, docstring const align_d, bool & hassep)
517 {
518         InsetTableCell tail = InsetTableCell(head);
519         tail.getText(0)->setMacrocontextPosition(
520                 head.getText(0)->macrocontextPosition());
521         tail.setBuffer(head.buffer());
522
523         DocIterator dit = doc_iterator_begin(&head.buffer(), &head);
524         for (; dit; dit.forwardChar())
525                 if (dit.inTexted() && dit.depth()==1
526                         && dit.paragraph().find(align_d, false, false, dit.pos()))
527                         break;
528
529         pit_type const psize = head.paragraphs().front().size();
530         hassep = dit;
531         if (hassep) {
532                 head.paragraphs().front().eraseChars(dit.pos(), psize, false);
533                 tail.paragraphs().front().eraseChars(0, 
534                         dit.pos() < psize ? dit.pos() + 1 : psize, false);
535         }
536
537         return tail;
538 }
539
540
541 /////////////////////////////////////////////////////////////////////
542 //
543 // Tabular
544 //
545 /////////////////////////////////////////////////////////////////////
546
547
548 Tabular::CellData::CellData(Buffer * buf)
549         : cellno(0),
550           width(0),
551           multicolumn(Tabular::CELL_NORMAL),
552           multirow(Tabular::CELL_NORMAL),
553           alignment(LYX_ALIGN_CENTER),
554           valignment(LYX_VALIGN_TOP),
555           decimal_hoffset(0),
556           decimal_width(0),
557           voffset(0),
558           top_line(false),
559           bottom_line(false),
560           left_line(false),
561           right_line(false),
562           usebox(BOX_NONE),
563           rotate(false),
564           inset(new InsetTableCell(buf))
565 {
566         inset->setBuffer(*buf);
567 }
568
569
570 Tabular::CellData::CellData(CellData const & cs)
571         : cellno(cs.cellno),
572           width(cs.width),
573           multicolumn(cs.multicolumn),
574           multirow(cs.multirow),
575           alignment(cs.alignment),
576           valignment(cs.valignment),
577           decimal_hoffset(cs.decimal_hoffset),
578           decimal_width(cs.decimal_width),
579           voffset(cs.voffset),
580           top_line(cs.top_line),
581           bottom_line(cs.bottom_line),
582           left_line(cs.left_line),
583           right_line(cs.right_line),
584           usebox(cs.usebox),
585           rotate(cs.rotate),
586           align_special(cs.align_special),
587           p_width(cs.p_width),
588           inset(static_cast<InsetTableCell *>(cs.inset->clone()))
589 {
590 }
591
592 Tabular::CellData & Tabular::CellData::operator=(CellData cs)
593 {
594         swap(cs);
595         return *this;
596 }
597
598 void Tabular::CellData::swap(CellData & rhs)
599 {
600         std::swap(cellno, rhs.cellno);
601         std::swap(width, rhs.width);
602         std::swap(multicolumn, rhs.multicolumn);
603         std::swap(multirow, rhs.multirow);
604         std::swap(alignment, rhs.alignment);
605         std::swap(valignment, rhs.valignment);
606         std::swap(decimal_hoffset, rhs.decimal_hoffset);
607         std::swap(decimal_width, rhs.decimal_width);
608         std::swap(voffset, rhs.voffset);
609         std::swap(top_line, rhs.top_line);
610         std::swap(bottom_line, rhs.bottom_line);
611         std::swap(left_line, rhs.left_line);
612         std::swap(right_line, rhs.right_line);
613         std::swap(usebox, rhs.usebox);
614         std::swap(rotate, rhs.rotate);
615         std::swap(align_special, rhs.align_special);
616         p_width.swap(rhs.p_width);
617         inset.swap(rhs.inset);
618 }
619
620
621 Tabular::RowData::RowData()
622         : ascent(0),
623           descent(0),
624           top_space_default(false),
625           bottom_space_default(false),
626           interline_space_default(false),
627           endhead(false),
628           endfirsthead(false),
629           endfoot(false),
630           endlastfoot(false),
631           newpage(false),
632           caption(false)
633 {}
634
635
636 Tabular::ColumnData::ColumnData()
637         : alignment(LYX_ALIGN_CENTER),
638           valignment(LYX_VALIGN_TOP),
639           width(0)
640 {
641 }
642
643
644 Tabular::ltType::ltType()
645         : topDL(false),
646           bottomDL(false),
647           empty(false)
648 {}
649
650
651 Tabular::Tabular(Buffer * buffer, row_type rows_arg, col_type columns_arg)
652 {
653         init(buffer, rows_arg, columns_arg);
654 }
655
656
657 void Tabular::setBuffer(Buffer & buffer)
658 {
659         buffer_ = &buffer;
660         for (row_type i = 0; i < nrows(); ++i)
661                 for (col_type j = 0; j < ncols(); ++j)
662                         cell_info[i][j].inset->setBuffer(*buffer_);
663 }
664
665
666 // activates all lines and sets all widths to 0
667 void Tabular::init(Buffer * buf, row_type rows_arg,
668                       col_type columns_arg)
669 {
670         buffer_ = buf;
671         row_info = row_vector(rows_arg);
672         column_info = column_vector(columns_arg);
673         cell_info = cell_vvector(rows_arg, cell_vector(columns_arg, CellData(buf)));
674         row_info.reserve(10);
675         column_info.reserve(10);
676         cell_info.reserve(100);
677         updateIndexes();
678         is_long_tabular = false;
679         tabular_valignment = LYX_VALIGN_MIDDLE;
680         longtabular_alignment = LYX_LONGTABULAR_ALIGN_CENTER;
681         rotate = false;
682         use_booktabs = false;
683         // set silly default lines
684         for (row_type r = 0; r < nrows(); ++r)
685                 for (col_type c = 0; c < ncols(); ++c) {
686                         cell_info[r][c].inset->setBuffer(*buffer_);
687                         cell_info[r][c].top_line = true;
688                         cell_info[r][c].left_line = true;
689                         cell_info[r][c].bottom_line = r == 0 || r == nrows() - 1;
690                         cell_info[r][c].right_line = c == ncols() - 1;
691                 }
692 }
693
694
695 void Tabular::appendRow(idx_type const cell)
696 {
697         row_type const row = cellRow(cell);
698
699         row_info.insert(row_info.begin() + row + 1, RowData());
700         row_info[row + 1] = row_info[row];
701
702         cell_info.insert(cell_info.begin() + row + 1,
703                 cell_vector(ncols(), CellData(buffer_)));
704         for (col_type c = 0; c < ncols(); ++c) {
705                 if (cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW)
706                         cell_info[row + 1][c].multirow = CELL_PART_OF_MULTIROW;
707                 else
708                         cell_info[row + 1][c].multirow = cell_info[row][c].multirow;
709         }
710         updateIndexes();
711
712         for (col_type c = 0; c < ncols(); ++c) {
713                 if (isPartOfMultiRow(row, c))
714                         continue;
715                 // inherit line settings
716                 idx_type const i = cellIndex(row + 1, c);
717                 idx_type const j = cellIndex(row, c);
718                 setLeftLine(i, leftLine(j));
719                 setRightLine(i, rightLine(j));
720                 setTopLine(i, topLine(j));
721                 if (topLine(j) && bottomLine(j)) {
722                         setBottomLine(i, true);
723                         setBottomLine(j, false);
724                 }
725                 // mark track changes
726                 if (buffer().params().trackChanges)
727                         cellInfo(i).inset->setChange(Change(Change::INSERTED));
728         }
729 }
730
731
732 void Tabular::deleteRow(row_type const row)
733 {
734         // Not allowed to delete last row
735         if (nrows() == 1)
736                 return;
737
738         for (col_type c = 0; c < ncols(); ++c) {
739                 // Care about multirow cells
740                 if (row + 1 < nrows() &&
741                     cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW &&
742                     cell_info[row + 1][c].multirow == CELL_PART_OF_MULTIROW) {
743                                 cell_info[row + 1][c].multirow = CELL_BEGIN_OF_MULTIROW;
744                 }
745         }
746         row_info.erase(row_info.begin() + row);
747         cell_info.erase(cell_info.begin() + row);
748         updateIndexes();
749 }
750
751
752 void Tabular::copyRow(row_type const row)
753 {
754         row_info.insert(row_info.begin() + row, row_info[row]);
755         cell_info.insert(cell_info.begin() + row, cell_info[row]);
756
757         if (buffer().params().trackChanges)
758                 for (col_type c = 0; c < ncols(); ++c)
759                         cell_info[row + 1][c].inset->setChange(Change(Change::INSERTED));
760
761         updateIndexes();
762 }
763
764
765 void Tabular::appendColumn(idx_type const cell)
766 {
767         col_type const c = cellColumn(cell);
768         
769         column_info.insert(column_info.begin() + c + 1, ColumnData());
770         column_info[c + 1] = column_info[c];
771
772         for (row_type r = 0; r < nrows(); ++r) {
773                 cell_info[r].insert(cell_info[r].begin() + c + 1, 
774                         CellData(buffer_));
775                 if (cell_info[r][c].multicolumn == CELL_BEGIN_OF_MULTICOLUMN)
776                         cell_info[r][c + 1].multicolumn = CELL_PART_OF_MULTICOLUMN;
777                 else
778                         cell_info[r][c + 1].multicolumn = cell_info[r][c].multicolumn;
779         }
780         updateIndexes();
781         for (row_type r = 0; r < nrows(); ++r) {
782                 // inherit line settings
783                 idx_type const i = cellIndex(r, c + 1);
784                 idx_type const j = cellIndex(r, c);
785                 setBottomLine(i, bottomLine(j));
786                 setTopLine(i, topLine(j));
787                 setLeftLine(i, leftLine(j));
788                 if (rightLine(j) && rightLine(j)) {
789                         setRightLine(i, true);
790                         setRightLine(j, false);
791                 }
792                 if (buffer().params().trackChanges)
793                         cellInfo(i).inset->setChange(Change(Change::INSERTED));
794         }
795 }
796
797
798 void Tabular::deleteColumn(col_type const col)
799 {
800         // Not allowed to delete last column
801         if (ncols() == 1)
802                 return;
803
804         for (row_type r = 0; r < nrows(); ++r) {
805                 // Care about multicolumn cells
806                 if (col + 1 < ncols() &&
807                     cell_info[r][col].multicolumn == CELL_BEGIN_OF_MULTICOLUMN &&
808                     cell_info[r][col + 1].multicolumn == CELL_PART_OF_MULTICOLUMN) {
809                                 cell_info[r][col + 1].multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
810                 }
811                 cell_info[r].erase(cell_info[r].begin() + col);
812         }
813         column_info.erase(column_info.begin() + col);
814         updateIndexes();
815 }
816
817
818 void Tabular::copyColumn(col_type const col)
819 {
820         BufferParams const & bp = buffer().params();
821         column_info.insert(column_info.begin() + col, column_info[col]);
822
823         for (row_type r = 0; r < nrows(); ++r) {
824                 cell_info[r].insert(cell_info[r].begin() + col, cell_info[r][col]);
825                 if (bp.trackChanges)
826                         cell_info[r][col + 1].inset->setChange(Change(Change::INSERTED));
827         }
828         updateIndexes();
829 }
830
831
832 void Tabular::updateIndexes()
833 {
834         setBuffer(buffer());
835         numberofcells = 0;
836         for (row_type row = 0; row < nrows(); ++row)
837                 for (col_type column = 0; column < ncols(); ++column) {
838                         if (!isPartOfMultiColumn(row, column)
839                                 && !isPartOfMultiRow(row, column))
840                                 ++numberofcells;
841                         if (isPartOfMultiRow(row, column))
842                                 cell_info[row][column].cellno = cell_info[row - 1][column].cellno;
843                         else
844                                 cell_info[row][column].cellno = numberofcells - 1;
845                 }
846
847         rowofcell.resize(numberofcells);
848         columnofcell.resize(numberofcells);
849         idx_type i = 0;
850         for (row_type row = 0; row < nrows(); ++row)
851                 for (col_type column = 0; column < ncols(); ++column) {
852                         if (isPartOfMultiColumn(row, column))
853                                 continue;
854                         setFixedWidth(row, column);
855                         cell_info[row][column].inset->setContentAlignment(
856                                 getAlignment(cellIndex(row, column)));
857                         if (!isPartOfMultiRow(row, column)) {
858                                 columnofcell[i] = column;
859                                 rowofcell[i] = row;
860                         } else
861                                 continue;
862                         ++i;
863                 }
864 }
865
866
867 Tabular::idx_type Tabular::numberOfCellsInRow(row_type const row) const
868 {
869         idx_type result = 0;
870         for (col_type c = 0; c < ncols(); ++c)
871                 if (cell_info[row][c].multicolumn != Tabular::CELL_PART_OF_MULTICOLUMN)
872                         ++result;
873         return result;
874 }
875
876
877 bool Tabular::topLine(idx_type const cell) const
878 {
879         return cellInfo(cell).top_line;
880 }
881
882
883 bool Tabular::bottomLine(idx_type const cell) const
884 {
885         return cellInfo(cell).bottom_line;
886 }
887
888
889 bool Tabular::leftLine(idx_type cell) const
890 {
891         if (use_booktabs)
892                 return false;
893         return cellInfo(cell).left_line;
894 }
895
896
897 bool Tabular::rightLine(idx_type cell) const
898 {
899         if (use_booktabs)
900                 return false;
901         return cellInfo(cell).right_line;
902 }
903
904
905 int Tabular::interRowSpace(row_type row) const
906 {
907         if (!row || row >= nrows())
908                 return 0;
909
910         int const interline_space = row_info[row - 1].interline_space_default ?
911                 default_line_space :
912                 row_info[row - 1].interline_space.inPixels(width());
913         if (rowTopLine(row) && rowBottomLine(row - 1))
914                 return interline_space + WIDTH_OF_LINE;
915         return interline_space;
916 }
917
918
919 int Tabular::interColumnSpace(idx_type cell) const
920 {
921         col_type const nextcol = cellColumn(cell) + columnSpan(cell);
922         if (rightLine(cell) && nextcol < ncols()
923                 && leftLine(cellIndex(cellRow(cell), nextcol)))
924                 return WIDTH_OF_LINE;
925         return 0;
926 }
927
928
929 int Tabular::cellWidth(idx_type cell) const
930 {
931         int w = 0;
932         col_type const span = columnSpan(cell);
933         col_type const col = cellColumn(cell);
934         for(col_type c = col; c < col + span ; ++c)
935                 w += column_info[c].width;
936         return w;
937 }
938
939
940 int Tabular::cellHeight(idx_type cell) const
941 {
942         row_type const span = rowSpan(cell);
943         row_type const row = cellRow(cell);
944         int h = 0;
945         for(row_type r = row; r < row + span ; ++r) {
946                 h += rowAscent(r) + rowDescent(r);
947                 if (r != row + span - 1)
948                         h += interRowSpace(r + 1);
949         }
950
951         return h;
952 }
953
954
955 bool Tabular::updateColumnWidths()
956 {
957         vector<int> max_dwidth(ncols(), 0);
958         for(col_type c = 0; c < ncols(); ++c)
959                 for(row_type r = 0; r < nrows(); ++r) {
960                         idx_type const i = cellIndex(r, c);
961                         if (getAlignment(i) == LYX_ALIGN_DECIMAL)
962                                 max_dwidth[c] = max(max_dwidth[c], cell_info[r][c].decimal_width);
963                 }
964
965         bool update = false;
966         // for each col get max of single col cells
967         for(col_type c = 0; c < ncols(); ++c) {
968                 int new_width = 0;
969                 for(row_type r = 0; r < nrows(); ++r) {
970                         idx_type const i = cellIndex(r, c);
971                         if (columnSpan(i) == 1) {
972                                 if (getAlignment(i) == LYX_ALIGN_DECIMAL
973                                         && cell_info[r][c].decimal_width!=0)
974                                         new_width = max(new_width, cellInfo(i).width 
975                                                 + max_dwidth[c] - cellInfo(i).decimal_width);
976                                 else
977                                         new_width = max(new_width, cellInfo(i).width);
978                         }
979                 }
980
981                 if (column_info[c].width != new_width) {
982                         column_info[c].width = new_width;
983                         update = true;
984                 }
985         }
986         // update col widths to fit merged cells
987         for(col_type c = 0; c < ncols(); ++c)
988                 for(row_type r = 0; r < nrows(); ++r) {
989                         idx_type const i = cellIndex(r, c);
990                         int const span = columnSpan(i);
991                         if (span == 1 || c > cellColumn(i))
992                                 continue;
993
994                         int old_width = 0;
995                         for(col_type j = c; j < c + span ; ++j)
996                                 old_width += column_info[j].width;
997
998                         if (cellInfo(i).width > old_width) {
999                                 column_info[c + span - 1].width += cellInfo(i).width - old_width;
1000                                 update = true;
1001                         }
1002                 }
1003
1004         return update;
1005 }
1006
1007
1008 int Tabular::width() const
1009 {
1010         int width = 0;
1011         for (col_type c = 0; c < ncols(); ++c)
1012                 width += column_info[c].width;
1013         return width;
1014 }
1015
1016
1017 void Tabular::setAlignment(idx_type cell, LyXAlignment align,
1018                               bool onlycolumn)
1019 {
1020         col_type const col = cellColumn(cell);
1021         if (onlycolumn || !isMultiColumn(cell)) {
1022                 column_info[col].alignment = align;
1023                 docstring & dpoint = column_info[col].decimal_point;
1024                 if (align == LYX_ALIGN_DECIMAL && dpoint.empty())
1025                         dpoint = from_utf8(lyxrc.default_decimal_point);
1026         } else {
1027                 cellInfo(cell).alignment = align;
1028                 cellInset(cell).get()->setContentAlignment(align);
1029         }
1030 }
1031
1032
1033 void Tabular::setVAlignment(idx_type cell, VAlignment align,
1034                                bool onlycolumn)
1035 {
1036         if (!isMultiColumn(cell) || onlycolumn)
1037                 column_info[cellColumn(cell)].valignment = align;
1038         if (!onlycolumn)
1039                 cellInfo(cell).valignment = align;
1040 }
1041
1042
1043 namespace {
1044
1045 /**
1046  * Allow line and paragraph breaks for fixed width cells or disallow them,
1047  * merge cell paragraphs and reset layout to standard for variable width
1048  * cells.
1049  */
1050 void toggleFixedWidth(Cursor & cur, InsetTableCell * inset, bool fixedWidth)
1051 {
1052         inset->setAutoBreakRows(fixedWidth);
1053         inset->toggleFixedWidth(fixedWidth);
1054         if (fixedWidth)
1055                 return;
1056
1057         // merge all paragraphs to one
1058         BufferParams const & bp = cur.bv().buffer().params();
1059         while (inset->paragraphs().size() > 1)
1060                 mergeParagraph(bp, inset->paragraphs(), 0);
1061
1062         // reset layout
1063         cur.push(*inset);
1064         // undo information has already been recorded
1065         inset->getText(0)->setLayout(0, cur.lastpit() + 1,
1066                         bp.documentClass().plainLayoutName());
1067         cur.pop();
1068 }
1069
1070 }
1071
1072
1073 void Tabular::setColumnPWidth(Cursor & cur, idx_type cell,
1074                 Length const & width)
1075 {
1076         col_type const c = cellColumn(cell);
1077
1078         column_info[c].p_width = width;
1079         // reset the vertical alignment to top if the fixed with
1080         // is removed or zero because only fixed width columns can
1081         // have a vertical alignment
1082         if (column_info[c].p_width.zero())
1083                 column_info[c].valignment = LYX_VALIGN_TOP;
1084         for (row_type r = 0; r < nrows(); ++r) {
1085                 idx_type const cell = cellIndex(r, c);
1086                 // because of multicolumns
1087                 toggleFixedWidth(cur, cellInset(cell).get(),
1088                                  !getPWidth(cell).zero());
1089         }
1090         // cur paragraph can become invalid after paragraphs were merged
1091         if (cur.pit() > cur.lastpit())
1092                 cur.pit() = cur.lastpit();
1093         // cur position can become invalid after newlines were removed
1094         if (cur.pos() > cur.lastpos())
1095                 cur.pos() = cur.lastpos();
1096 }
1097
1098
1099 bool Tabular::setFixedWidth(row_type r, col_type c)
1100 {
1101         bool const multicol = cell_info[r][c].multicolumn != CELL_NORMAL;
1102         bool const fixed_width = (!column_info[c].p_width.zero() && !multicol)
1103               || (multicol && !cell_info[r][c].p_width.zero());
1104         cell_info[r][c].inset->toggleFixedWidth(fixed_width);
1105         return fixed_width;
1106 }
1107
1108
1109 bool Tabular::setMColumnPWidth(Cursor & cur, idx_type cell,
1110                 Length const & width)
1111 {
1112         if (!isMultiColumn(cell))
1113                 return false;
1114
1115         cellInfo(cell).p_width = width;
1116         toggleFixedWidth(cur, cellInset(cell).get(), !width.zero());
1117         // cur paragraph can become invalid after paragraphs were merged
1118         if (cur.pit() > cur.lastpit())
1119                 cur.pit() = cur.lastpit();
1120         // cur position can become invalid after newlines were removed
1121         if (cur.pos() > cur.lastpos())
1122                 cur.pos() = cur.lastpos();
1123         return true;
1124 }
1125
1126
1127 void Tabular::setAlignSpecial(idx_type cell, docstring const & special,
1128                                  Tabular::Feature what)
1129 {
1130         if (what == SET_SPECIAL_MULTICOLUMN)
1131                 cellInfo(cell).align_special = special;
1132         else
1133                 column_info[cellColumn(cell)].align_special = special;
1134 }
1135
1136
1137 void Tabular::setTopLine(idx_type i, bool line)
1138 {
1139         cellInfo(i).top_line = line;
1140 }
1141
1142
1143 void Tabular::setBottomLine(idx_type i, bool line)
1144 {
1145         cellInfo(i).bottom_line = line;
1146 }
1147
1148
1149 void Tabular::setLeftLine(idx_type cell, bool line)
1150 {
1151         cellInfo(cell).left_line = line;
1152 }
1153
1154
1155 void Tabular::setRightLine(idx_type cell, bool line)
1156 {
1157         cellInfo(cell).right_line = line;
1158 }
1159
1160 bool Tabular::rowTopLine(row_type r) const
1161 {
1162         bool all_rows_set = true;
1163         for (col_type c = 0; all_rows_set && c < ncols(); ++c)
1164                 all_rows_set = cellInfo(cellIndex(r, c)).top_line;
1165         return all_rows_set;
1166 }
1167
1168
1169 bool Tabular::rowBottomLine(row_type r) const
1170 {
1171         bool all_rows_set = true;
1172         for (col_type c = 0; all_rows_set && c < ncols(); ++c)
1173                 all_rows_set = cellInfo(cellIndex(r, c)).bottom_line;
1174         return all_rows_set;
1175 }
1176
1177
1178 bool Tabular::columnLeftLine(col_type c) const
1179 {
1180         if (use_booktabs)
1181                 return false;
1182
1183         int nrows_left = 0;
1184         int total = 0;
1185         for (row_type r = 0; r < nrows(); ++r) {
1186                 idx_type const i = cellIndex(r, c);
1187                 if (c == cellColumn(i)) {
1188                         ++total;
1189                         bool right = c > 0 && cellInfo(cellIndex(r, c - 1)).right_line;
1190                         if (cellInfo(i).left_line || right)
1191                                 ++nrows_left;
1192                 }
1193         }
1194         return 2 * nrows_left >= total;
1195 }
1196
1197
1198 bool Tabular::columnRightLine(col_type c) const
1199 {
1200         if (use_booktabs)
1201                 return false;
1202
1203         int nrows_right = 0;
1204         int total = 0;
1205         for (row_type r = 0; r < nrows(); ++r) {
1206                 idx_type i = cellIndex(r, c);
1207                 if (c == cellColumn(i) + columnSpan(i) - 1) {
1208                         ++total;
1209                         bool left = (c + 1 < ncols() 
1210                                 && cellInfo(cellIndex(r, c + 1)).left_line)
1211                                 || c + 1 == ncols();
1212                         if (cellInfo(i).right_line && left)
1213                                 ++nrows_right;
1214                 }
1215         }
1216         return 2 * nrows_right >= total;
1217 }
1218
1219
1220 LyXAlignment Tabular::getAlignment(idx_type cell, bool onlycolumn) const
1221 {
1222         if (!onlycolumn && (isMultiColumn(cell) || isMultiRow(cell)))
1223                 return cellInfo(cell).alignment;
1224         
1225         return column_info[cellColumn(cell)].alignment;
1226 }
1227
1228
1229 Tabular::VAlignment
1230 Tabular::getVAlignment(idx_type cell, bool onlycolumn) const
1231 {
1232         if (!onlycolumn && (isMultiColumn(cell) || isMultiRow(cell)))
1233                 return cellInfo(cell).valignment;
1234         return column_info[cellColumn(cell)].valignment;
1235 }
1236
1237
1238 Length const Tabular::getPWidth(idx_type cell) const
1239 {
1240         if (isMultiColumn(cell))
1241                 return cellInfo(cell).p_width;
1242         return column_info[cellColumn(cell)].p_width;
1243 }
1244
1245
1246 int Tabular::textHOffset(idx_type cell) const
1247 {
1248         // the LaTeX Way :-(
1249         int x = WIDTH_OF_LINE;
1250
1251         int const w = cellWidth(cell) - cellInfo(cell).width;
1252
1253         switch (getAlignment(cell)) {
1254         case LYX_ALIGN_CENTER:
1255                 x += w / 2;
1256                 break;
1257         case LYX_ALIGN_RIGHT:
1258                 x += w;
1259                 break;
1260         case LYX_ALIGN_DECIMAL: {
1261                 // we center when no decimal point
1262                 if (cellInfo(cell).decimal_width == 0) {
1263                         x += w / 2;
1264                         break;
1265                 }
1266                 col_type const c = cellColumn(cell);
1267                 int max_dhoffset = 0;
1268                 for(row_type r = 0; r < row_info.size() ; ++r) {
1269                         idx_type const i = cellIndex(r, c);
1270                         if (getAlignment(i) == LYX_ALIGN_DECIMAL
1271                                 && cellInfo(i).decimal_width != 0)
1272                                 max_dhoffset = max(max_dhoffset, cellInfo(i).decimal_hoffset);
1273                 }
1274                 x += max_dhoffset - cellInfo(cell).decimal_hoffset;
1275         }
1276         default:
1277                 // LYX_ALIGN_LEFT: nothing :-)
1278                 break;
1279         }
1280
1281         return x;
1282 }
1283
1284
1285 int Tabular::textVOffset(idx_type cell) const
1286 {
1287         int voffset = cellInfo(cell).voffset;
1288         if (isMultiRow(cell)) {
1289                 row_type const row = cellRow(cell);
1290                 voffset += (cellHeight(cell) - rowAscent(row) - rowDescent(row))/2; 
1291         }
1292         return voffset;
1293 }
1294
1295
1296 Tabular::idx_type Tabular::getFirstCellInRow(row_type row) const
1297 {
1298         col_type c = 0;
1299         while (cell_info[row][c].multirow == CELL_PART_OF_MULTIROW)
1300                 ++c;
1301         return cell_info[row][c].cellno;
1302 }
1303
1304
1305 Tabular::idx_type Tabular::getLastCellInRow(row_type row) const
1306 {
1307         col_type c = ncols() - 1;
1308         while (cell_info[row][c].multirow == CELL_PART_OF_MULTIROW
1309                 || cell_info[row][c].multicolumn == CELL_PART_OF_MULTICOLUMN)
1310                 --c;
1311         return cell_info[row][c].cellno;
1312 }
1313
1314
1315 Tabular::row_type Tabular::cellRow(idx_type cell) const
1316 {
1317         if (cell >= numberofcells)
1318                 return nrows() - 1;
1319         if (cell == npos)
1320                 return 0;
1321         return rowofcell[cell];
1322 }
1323
1324
1325 Tabular::col_type Tabular::cellColumn(idx_type cell) const
1326 {
1327         if (cell >= numberofcells)
1328                 return ncols() - 1;
1329         if (cell == npos)
1330                 return 0;       
1331         return columnofcell[cell];
1332 }
1333
1334
1335 void Tabular::write(ostream & os) const
1336 {
1337         // header line
1338         os << "<lyxtabular"
1339            << write_attribute("version", 3)
1340            << write_attribute("rows", nrows())
1341            << write_attribute("columns", ncols())
1342            << ">\n";
1343         // global longtable options
1344         os << "<features"
1345            << write_attribute("rotate", rotate)
1346            << write_attribute("booktabs", use_booktabs)
1347            << write_attribute("islongtable", is_long_tabular)
1348            << write_attribute("firstHeadTopDL", endfirsthead.topDL)
1349            << write_attribute("firstHeadBottomDL", endfirsthead.bottomDL)
1350            << write_attribute("firstHeadEmpty", endfirsthead.empty)
1351            << write_attribute("headTopDL", endhead.topDL)
1352            << write_attribute("headBottomDL", endhead.bottomDL)
1353            << write_attribute("footTopDL", endfoot.topDL)
1354            << write_attribute("footBottomDL", endfoot.bottomDL)
1355            << write_attribute("lastFootTopDL", endlastfoot.topDL)
1356            << write_attribute("lastFootBottomDL", endlastfoot.bottomDL)
1357            << write_attribute("lastFootEmpty", endlastfoot.empty);
1358         // longtables cannot be aligned vertically
1359         if (!is_long_tabular)
1360            os << write_attribute("tabularvalignment", tabular_valignment);
1361         if (is_long_tabular)
1362            os << write_attribute("longtabularalignment",
1363                                  longtabular_alignment);
1364         os << ">\n";
1365         for (col_type c = 0; c < ncols(); ++c) {
1366                 os << "<column"
1367                    << write_attribute("alignment", column_info[c].alignment);
1368                 if (column_info[c].alignment == LYX_ALIGN_DECIMAL)
1369                    os << write_attribute("decimal_point", column_info[c].decimal_point);
1370                 os << write_attribute("valignment", column_info[c].valignment)
1371                    << write_attribute("width", column_info[c].p_width.asString())
1372                    << write_attribute("special", column_info[c].align_special)
1373                    << ">\n";
1374         }
1375         for (row_type r = 0; r < nrows(); ++r) {
1376                 static const string def("default");
1377                 os << "<row";
1378                 if (row_info[r].top_space_default)
1379                         os << write_attribute("topspace", def);
1380                 else
1381                         os << write_attribute("topspace", row_info[r].top_space);
1382                 if (row_info[r].bottom_space_default)
1383                         os << write_attribute("bottomspace", def);
1384                 else
1385                         os << write_attribute("bottomspace", row_info[r].bottom_space);
1386                 if (row_info[r].interline_space_default)
1387                         os << write_attribute("interlinespace", def);
1388                 else
1389                         os << write_attribute("interlinespace", row_info[r].interline_space);
1390                 os << write_attribute("endhead", row_info[r].endhead)
1391                    << write_attribute("endfirsthead", row_info[r].endfirsthead)
1392                    << write_attribute("endfoot", row_info[r].endfoot)
1393                    << write_attribute("endlastfoot", row_info[r].endlastfoot)
1394                    << write_attribute("newpage", row_info[r].newpage)
1395                    << write_attribute("caption", row_info[r].caption)
1396                    << ">\n";
1397                 for (col_type c = 0; c < ncols(); ++c) {
1398                         os << "<cell"
1399                            << write_attribute("multicolumn", cell_info[r][c].multicolumn)
1400                            << write_attribute("multirow", cell_info[r][c].multirow)
1401                            << write_attribute("alignment", cell_info[r][c].alignment)
1402                            << write_attribute("valignment", cell_info[r][c].valignment)
1403                            << write_attribute("topline", cell_info[r][c].top_line)
1404                            << write_attribute("bottomline", cell_info[r][c].bottom_line)
1405                            << write_attribute("leftline", cell_info[r][c].left_line)
1406                            << write_attribute("rightline", cell_info[r][c].right_line)
1407                            << write_attribute("rotate", cell_info[r][c].rotate)
1408                            << write_attribute("usebox", cell_info[r][c].usebox)
1409                            << write_attribute("width", cell_info[r][c].p_width)
1410                            << write_attribute("special", cell_info[r][c].align_special)
1411                            << ">\n";
1412                         os << "\\begin_inset ";
1413                         cell_info[r][c].inset->write(os);
1414                         os << "\n\\end_inset\n"
1415                            << "</cell>\n";
1416                 }
1417                 os << "</row>\n";
1418         }
1419         os << "</lyxtabular>\n";
1420 }
1421
1422
1423 void Tabular::read(Lexer & lex)
1424 {
1425         string line;
1426         istream & is = lex.getStream();
1427
1428         l_getline(is, line);
1429         if (!prefixIs(line, "<lyxtabular ") && !prefixIs(line, "<Tabular ")) {
1430                 LASSERT(false, /**/);
1431                 return;
1432         }
1433
1434         int version;
1435         if (!getTokenValue(line, "version", version))
1436                 return;
1437         LASSERT(version >= 2, /**/);
1438
1439         int rows_arg;
1440         if (!getTokenValue(line, "rows", rows_arg))
1441                 return;
1442         int columns_arg;
1443         if (!getTokenValue(line, "columns", columns_arg))
1444                 return;
1445         init(buffer_, rows_arg, columns_arg);
1446         l_getline(is, line);
1447         if (!prefixIs(line, "<features")) {
1448                 lyxerr << "Wrong tabular format (expected <features ...> got"
1449                        << line << ')' << endl;
1450                 return;
1451         }
1452         getTokenValue(line, "rotate", rotate);
1453         getTokenValue(line, "booktabs", use_booktabs);
1454         getTokenValue(line, "islongtable", is_long_tabular);
1455         getTokenValue(line, "tabularvalignment", tabular_valignment);
1456         getTokenValue(line, "longtabularalignment", longtabular_alignment);
1457         getTokenValue(line, "firstHeadTopDL", endfirsthead.topDL);
1458         getTokenValue(line, "firstHeadBottomDL", endfirsthead.bottomDL);
1459         getTokenValue(line, "firstHeadEmpty", endfirsthead.empty);
1460         getTokenValue(line, "headTopDL", endhead.topDL);
1461         getTokenValue(line, "headBottomDL", endhead.bottomDL);
1462         getTokenValue(line, "footTopDL", endfoot.topDL);
1463         getTokenValue(line, "footBottomDL", endfoot.bottomDL);
1464         getTokenValue(line, "lastFootTopDL", endlastfoot.topDL);
1465         getTokenValue(line, "lastFootBottomDL", endlastfoot.bottomDL);
1466         getTokenValue(line, "lastFootEmpty", endlastfoot.empty);
1467
1468         for (col_type c = 0; c < ncols(); ++c) {
1469                 l_getline(is,line);
1470                 if (!prefixIs(line,"<column")) {
1471                         lyxerr << "Wrong tabular format (expected <column ...> got"
1472                                << line << ')' << endl;
1473                         return;
1474                 }
1475                 getTokenValue(line, "alignment", column_info[c].alignment);
1476                 getTokenValue(line, "decimal_point", column_info[c].decimal_point);
1477                 getTokenValue(line, "valignment", column_info[c].valignment);
1478                 getTokenValue(line, "width", column_info[c].p_width);
1479                 getTokenValue(line, "special", column_info[c].align_special);
1480         }
1481
1482         for (row_type i = 0; i < nrows(); ++i) {
1483                 l_getline(is, line);
1484                 if (!prefixIs(line, "<row")) {
1485                         lyxerr << "Wrong tabular format (expected <row ...> got"
1486                                << line << ')' << endl;
1487                         return;
1488                 }
1489                 getTokenValue(line, "topspace", row_info[i].top_space,
1490                               row_info[i].top_space_default);
1491                 getTokenValue(line, "bottomspace", row_info[i].bottom_space,
1492                               row_info[i].bottom_space_default);
1493                 getTokenValue(line, "interlinespace", row_info[i].interline_space,
1494                               row_info[i].interline_space_default);
1495                 getTokenValue(line, "endfirsthead", row_info[i].endfirsthead);
1496                 getTokenValue(line, "endhead", row_info[i].endhead);
1497                 getTokenValue(line, "endfoot", row_info[i].endfoot);
1498                 getTokenValue(line, "endlastfoot", row_info[i].endlastfoot);
1499                 getTokenValue(line, "newpage", row_info[i].newpage);
1500                 getTokenValue(line, "caption", row_info[i].caption);
1501                 for (col_type j = 0; j < ncols(); ++j) {
1502                         l_getline(is, line);
1503                         if (!prefixIs(line, "<cell")) {
1504                                 lyxerr << "Wrong tabular format (expected <cell ...> got"
1505                                        << line << ')' << endl;
1506                                 return;
1507                         }
1508                         getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn);
1509                         getTokenValue(line, "multirow", cell_info[i][j].multirow);
1510                         getTokenValue(line, "alignment", cell_info[i][j].alignment);
1511                         getTokenValue(line, "valignment", cell_info[i][j].valignment);
1512                         getTokenValue(line, "topline", cell_info[i][j].top_line);
1513                         getTokenValue(line, "bottomline", cell_info[i][j].bottom_line);
1514                         getTokenValue(line, "leftline", cell_info[i][j].left_line);
1515                         getTokenValue(line, "rightline", cell_info[i][j].right_line);
1516                         getTokenValue(line, "rotate", cell_info[i][j].rotate);
1517                         getTokenValue(line, "usebox", cell_info[i][j].usebox);
1518                         getTokenValue(line, "width", cell_info[i][j].p_width);
1519                         setFixedWidth(i,j);
1520                         getTokenValue(line, "special", cell_info[i][j].align_special);
1521                         l_getline(is, line);
1522                         if (prefixIs(line, "\\begin_inset")) {
1523                                 cell_info[i][j].inset->setBuffer(*buffer_);
1524                                 cell_info[i][j].inset->read(lex);
1525                                 l_getline(is, line);
1526                         }
1527                         if (!prefixIs(line, "</cell>")) {
1528                                 lyxerr << "Wrong tabular format (expected </cell> got"
1529                                        << line << ')' << endl;
1530                                 return;
1531                         }
1532                 }
1533                 l_getline(is, line);
1534                 if (!prefixIs(line, "</row>")) {
1535                         lyxerr << "Wrong tabular format (expected </row> got"
1536                                << line << ')' << endl;
1537                         return;
1538                 }
1539         }
1540         while (!prefixIs(line, "</lyxtabular>")) {
1541                 l_getline(is, line);
1542         }
1543         updateIndexes();
1544 }
1545
1546
1547 bool Tabular::isMultiColumn(idx_type cell) const
1548 {
1549         return (cellInfo(cell).multicolumn == CELL_BEGIN_OF_MULTICOLUMN 
1550                 || cellInfo(cell).multicolumn == CELL_PART_OF_MULTICOLUMN);
1551 }
1552
1553
1554 Tabular::CellData & Tabular::cellInfo(idx_type cell) const
1555 {
1556         return cell_info[cellRow(cell)][cellColumn(cell)];
1557 }
1558
1559
1560 Tabular::idx_type Tabular::setMultiColumn(idx_type cell, idx_type number)
1561 {
1562         idx_type const col = cellColumn(cell);
1563         idx_type const row = cellRow(cell);
1564         for (idx_type i = 0; i < number; ++i)
1565                 unsetMultiRow(cellIndex(row, col + i));
1566
1567         // unsetting of multirow may have invalidated cell index
1568         cell = cellIndex(row, col);
1569         CellData & cs = cellInfo(cell);
1570         cs.multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
1571         if (column_info[col].alignment != LYX_ALIGN_DECIMAL)
1572                 cs.alignment = column_info[col].alignment;
1573         if (col > 0)
1574                 setRightLine(cell, rightLine(cellIndex(row, col - 1)));
1575
1576         for (idx_type i = 1; i < number; ++i) {
1577                 CellData & cs1 = cellInfo(cell + i);
1578                 cs1.multicolumn = CELL_PART_OF_MULTICOLUMN;
1579                 cs.inset->appendParagraphs(cs1.inset->paragraphs());
1580                 cs1.inset->clear();
1581         }
1582         updateIndexes();
1583         return cell;
1584 }
1585
1586
1587 bool Tabular::isMultiRow(idx_type cell) const
1588 {
1589         return (cellInfo(cell).multirow == CELL_BEGIN_OF_MULTIROW
1590                 || cellInfo(cell).multirow == CELL_PART_OF_MULTIROW);
1591 }
1592
1593
1594 Tabular::idx_type Tabular::setMultiRow(idx_type cell, idx_type number)
1595 {
1596         idx_type const col = cellColumn(cell);
1597         idx_type const row = cellRow(cell);
1598         for (idx_type i = 0; i < number; ++i)
1599                 unsetMultiColumn(cellIndex(row + i, col));
1600
1601         // unsetting of multirow may have invalidated cell index
1602         cell = cellIndex(row, col);
1603         CellData & cs = cellInfo(cell);
1604         cs.multirow = CELL_BEGIN_OF_MULTIROW;
1605         cs.valignment = LYX_VALIGN_MIDDLE;
1606         // the horizontal alignment of multirow cells can only
1607         // be changed for the whole table row,
1608         // support changing this only for the multirow cell can be done via
1609         // \multirowsetup
1610         // this feature would be a fileformat change
1611         // until LyX supports this, use the deault alignment of multirow
1612         // cells: left
1613         cs.alignment = LYX_ALIGN_CENTER; 
1614
1615         // set the bottom row of the last selected cell
1616         setBottomLine(cell, bottomLine(cell + (number - 1)*ncols()));
1617
1618         for (idx_type i = 1; i < number; ++i) {
1619                 CellData & cs1 = cell_info[row + i][col];
1620                 cs1.multirow = CELL_PART_OF_MULTIROW;
1621                 cs.inset->appendParagraphs(cs1.inset->paragraphs());
1622                 cs1.inset->clear();
1623         }
1624         updateIndexes();
1625         return cell;
1626 }
1627
1628
1629 Tabular::idx_type Tabular::columnSpan(idx_type cell) const
1630 {
1631         row_type const row = cellRow(cell);
1632         col_type const col = cellColumn(cell);
1633         int span = 1;
1634         while (col + span < ncols() && isPartOfMultiColumn(row, col + span))
1635                 ++span;
1636
1637         return span;
1638 }
1639
1640
1641 Tabular::idx_type Tabular::rowSpan(idx_type cell) const
1642 {
1643         col_type const column = cellColumn(cell);
1644         col_type row = cellRow(cell) + 1;
1645         while (row < nrows() && isPartOfMultiRow(row, column))
1646                 ++row;
1647         
1648         return row - cellRow(cell);
1649 }
1650
1651
1652 void Tabular::unsetMultiColumn(idx_type cell)
1653 {
1654         if (!isMultiColumn(cell))
1655                 return;
1656
1657         row_type const row = cellRow(cell);
1658         col_type const col = cellColumn(cell);
1659         row_type const span = columnSpan(cell);
1660         for (col_type c = 0; c < span; ++c)
1661                 cell_info[row][col + c].multicolumn = CELL_NORMAL;
1662         updateIndexes();
1663 }
1664
1665
1666 void Tabular::unsetMultiRow(idx_type cell)
1667 {
1668         if (!isMultiRow(cell))
1669                 return;
1670
1671         cellInfo(cell).valignment = LYX_VALIGN_TOP;
1672         cellInfo(cell).alignment = LYX_ALIGN_CENTER;
1673         row_type const row = cellRow(cell);
1674         col_type const col = cellColumn(cell);
1675         row_type const span = rowSpan(cell);
1676         for (row_type r = 0; r < span; ++r)
1677                 cell_info[row + r][col].multirow = CELL_NORMAL;
1678         updateIndexes();
1679 }
1680
1681
1682 void Tabular::setRotateCell(idx_type cell, bool flag)
1683 {
1684         cellInfo(cell).rotate = flag;
1685 }
1686
1687
1688 bool Tabular::getRotateCell(idx_type cell) const
1689 {
1690         return cellInfo(cell).rotate;
1691 }
1692
1693
1694 bool Tabular::needRotating() const
1695 {
1696         if (rotate)
1697                 return true;
1698         for (row_type r = 0; r < nrows(); ++r)
1699                 for (col_type c = 0; c < ncols(); ++c)
1700                         if (cell_info[r][c].rotate)
1701                                 return true;
1702         return false;
1703 }
1704
1705
1706 bool Tabular::isLastCell(idx_type cell) const
1707 {
1708         if (cell + 1 < numberofcells)
1709                 return false;
1710         return true;
1711 }
1712
1713
1714 Tabular::idx_type Tabular::cellAbove(idx_type cell) const
1715 {
1716         if (cellRow(cell) == 0)
1717                 return cell;
1718         
1719         col_type const col = cellColumn(cell);
1720         row_type r = cellRow(cell) - 1;
1721         while (r > 0 && cell_info[r][col].multirow == CELL_PART_OF_MULTIROW)
1722                 --r;
1723
1724         return cell_info[r][col].cellno;
1725 }
1726
1727
1728 Tabular::idx_type Tabular::cellBelow(idx_type cell) const
1729 {
1730         row_type const nextrow = cellRow(cell) + rowSpan(cell);
1731         if (nextrow < nrows())
1732                 return cell_info[nextrow][cellColumn(cell)].cellno;
1733         return cell;
1734 }
1735
1736
1737 Tabular::idx_type Tabular::cellIndex(row_type row, col_type column) const
1738 {
1739         LASSERT(column != npos && column < ncols()
1740                 && row != npos && row < nrows(), /**/);
1741         return cell_info[row][column].cellno;
1742 }
1743
1744
1745 void Tabular::setUsebox(idx_type cell, BoxType type)
1746 {
1747         cellInfo(cell).usebox = type;
1748 }
1749
1750
1751 // FIXME: Remove this routine because we cannot insert \parboxes when the user
1752 // adds line breaks, see bug 4886.
1753 Tabular::BoxType Tabular::getUsebox(idx_type cell) const
1754 {
1755         if ((!column_info[cellColumn(cell)].p_width.zero() && !isMultiColumn(cell)) ||
1756                 (isMultiColumn(cell) && !cellInfo(cell).p_width.zero()))
1757                 return BOX_NONE;
1758         if (cellInfo(cell).usebox > 1)
1759                 return cellInfo(cell).usebox;
1760         return useParbox(cell);
1761 }
1762
1763
1764 ///
1765 //  This are functions used for the longtable support
1766 ///
1767 void Tabular::setLTHead(row_type row, bool flag, ltType const & hd,
1768                            bool first)
1769 {
1770         if (first) {
1771                 endfirsthead = hd;
1772                 if (hd.set)
1773                         row_info[row].endfirsthead = flag;
1774         } else {
1775                 endhead = hd;
1776                 if (hd.set)
1777                         row_info[row].endhead = flag;
1778         }
1779 }
1780
1781
1782 bool Tabular::getRowOfLTHead(row_type row, ltType & hd) const
1783 {
1784         hd = endhead;
1785         hd.set = haveLTHead();
1786         return row_info[row].endhead;
1787 }
1788
1789
1790 bool Tabular::getRowOfLTFirstHead(row_type row, ltType & hd) const
1791 {
1792         hd = endfirsthead;
1793         hd.set = haveLTFirstHead();
1794         return row_info[row].endfirsthead;
1795 }
1796
1797
1798 void Tabular::setLTFoot(row_type row, bool flag, ltType const & fd,
1799                            bool last)
1800 {
1801         if (last) {
1802                 endlastfoot = fd;
1803                 if (fd.set)
1804                         row_info[row].endlastfoot = flag;
1805         } else {
1806                 endfoot = fd;
1807                 if (fd.set)
1808                         row_info[row].endfoot = flag;
1809         }
1810 }
1811
1812
1813 bool Tabular::getRowOfLTFoot(row_type row, ltType & fd) const
1814 {
1815         fd = endfoot;
1816         fd.set = haveLTFoot();
1817         return row_info[row].endfoot;
1818 }
1819
1820
1821 bool Tabular::getRowOfLTLastFoot(row_type row, ltType & fd) const
1822 {
1823         fd = endlastfoot;
1824         fd.set = haveLTLastFoot();
1825         return row_info[row].endlastfoot;
1826 }
1827
1828
1829 void Tabular::setLTNewPage(row_type row, bool what)
1830 {
1831         row_info[row].newpage = what;
1832 }
1833
1834
1835 bool Tabular::getLTNewPage(row_type row) const
1836 {
1837         return row_info[row].newpage;
1838 }
1839
1840
1841 bool Tabular::haveLTHead() const
1842 {
1843         for (row_type i = 0; i < nrows(); ++i)
1844                 if (row_info[i].endhead)
1845                         return true;
1846         return false;
1847 }
1848
1849
1850 bool Tabular::haveLTFirstHead() const
1851 {
1852         if (endfirsthead.empty)
1853                 return false;
1854         for (row_type r = 0; r < nrows(); ++r)
1855                 if (row_info[r].endfirsthead)
1856                         return true;
1857         return false;
1858 }
1859
1860
1861 bool Tabular::haveLTFoot() const
1862 {
1863         for (row_type r = 0; r < nrows(); ++r)
1864                 if (row_info[r].endfoot)
1865                         return true;
1866         return false;
1867 }
1868
1869
1870 bool Tabular::haveLTLastFoot() const
1871 {
1872         if (endlastfoot.empty)
1873                 return false;
1874         for (row_type r = 0; r < nrows(); ++r)
1875                 if (row_info[r].endlastfoot)
1876                         return true;
1877         return false;
1878 }
1879
1880
1881 Tabular::idx_type Tabular::setLTCaption(row_type row, bool what)
1882 {
1883         idx_type i = getFirstCellInRow(row);
1884         if (what) {
1885                 setMultiColumn(i, numberOfCellsInRow(row));
1886                 setTopLine(i, false);
1887                 setBottomLine(i, false);
1888                 setLeftLine(i, false);
1889                 setRightLine(i, false);
1890         } else {
1891                 unsetMultiColumn(i);
1892                 // When unsetting a caption row, also all existing
1893                 // captions in this row must be dissolved.
1894         }
1895         row_info[row].caption = what;
1896         return i;
1897 }
1898
1899
1900 bool Tabular::ltCaption(row_type row) const
1901 {
1902         return row_info[row].caption;
1903 }
1904
1905
1906 bool Tabular::haveLTCaption() const
1907 {
1908         for (row_type r = 0; r < nrows(); ++r)
1909                 if (row_info[r].caption)
1910                         return true;
1911         return false;
1912 }
1913
1914
1915 // end longtable support functions
1916
1917 void Tabular::setRowAscent(row_type row, int height)
1918 {
1919         if (row >= nrows() || row_info[row].ascent == height)
1920                 return;
1921         row_info[row].ascent = height;
1922 }
1923
1924
1925 void Tabular::setRowDescent(row_type row, int height)
1926 {
1927         if (row >= nrows() || row_info[row].descent == height)
1928                 return;
1929         row_info[row].descent = height;
1930 }
1931
1932
1933 int Tabular::rowAscent(row_type row) const
1934 {
1935         LASSERT(row < nrows(), /**/);
1936         return row_info[row].ascent;
1937 }
1938
1939
1940 int Tabular::rowDescent(row_type row) const
1941 {
1942         LASSERT(row < nrows(), /**/);
1943         return row_info[row].descent;
1944 }
1945
1946
1947 int Tabular::height() const
1948 {
1949         int height = 0;
1950         for (row_type row = 0; row < nrows(); ++row)
1951                 height += rowAscent(row) + rowDescent(row) +
1952                         interRowSpace(row);
1953         return height;
1954 }
1955
1956
1957 bool Tabular::isPartOfMultiColumn(row_type row, col_type column) const
1958 {
1959         LASSERT(row < nrows(), /**/);
1960         LASSERT(column < ncols(), /**/);
1961         return cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN;
1962 }
1963
1964
1965 bool Tabular::isPartOfMultiRow(row_type row, col_type column) const
1966 {
1967         LASSERT(row < nrows(), /**/);
1968         LASSERT(column < ncols(), /**/);
1969         return cell_info[row][column].multirow == CELL_PART_OF_MULTIROW;
1970 }
1971
1972
1973 int Tabular::TeXTopHLine(odocstream & os, row_type row, string const lang) const
1974 {
1975         // we only output complete row lines and the 1st row here, the rest
1976         // is done in Tabular::TeXBottomHLine(...)
1977
1978         // get for each column the topline (if any)
1979         vector<bool> topline;
1980         col_type nset = 0;
1981         for (col_type c = 0; c < ncols(); ++c) {
1982                 topline.push_back(topLine(cellIndex(row, c)));
1983                 // If cell is part of a multirow and not the first cell of the
1984                 // multirow, no line must be drawn.
1985                 if (row != 0)
1986                         if (isMultiRow(cellIndex(row, c))
1987                                 && isMultiRow(cellIndex(row - 1, c)))
1988                                         topline[c] = false;
1989                 if (topline[c])
1990                         ++nset;
1991         }
1992
1993         // do nothing if empty first row, or incomplete row line after
1994         if ((row == 0 && nset == 0) || (row > 0 && nset != ncols()))
1995                 return 0;
1996
1997         // only output complete row lines and the 1st row's clines
1998         if (nset == ncols()) {
1999                 if (use_booktabs) {
2000                         os << (row == 0 ? "\\toprule " : "\\midrule ");
2001                 } else {
2002                         os << "\\hline ";
2003                 }
2004         } else if (row == 0) {
2005                 for (col_type c = 0; c < ncols(); ++c) {
2006                         if (topline[c]) {
2007                                 col_type offset = 0;
2008                                 for (col_type j = 0 ; j < c; ++j)
2009                                         if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
2010                                                 ++offset;
2011                                 
2012                                 //babel makes the "-" character an active one, so we have to suppress this here
2013                                 //see http://groups.google.com/group/comp.text.tex/browse_thread/thread/af769424a4a0f289#
2014                                 if (lang == "slovak" || lang == "czech")
2015                                         os << "\\expandafter" << (use_booktabs ? "\\cmidrule" : "\\cline") 
2016                                         << "\\expandafter{\\expandafter" << c + 1 + offset << "\\string-";
2017                                 else
2018                                         os << (use_booktabs ? "\\cmidrule{" : "\\cline{") << c + 1 + offset << '-';
2019                                 
2020                                 col_type cstart = c;
2021                                 for ( ; c < ncols() && topline[c]; ++c) {}
2022                                 
2023                                 for (col_type j = cstart ; j < c ; ++j)
2024                                         if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
2025                                                 ++offset;
2026                                 
2027                                 os << c + offset << "} ";
2028                         }
2029                 }
2030         }
2031         os << "\n";
2032         return 1;
2033 }
2034
2035
2036 int Tabular::TeXBottomHLine(odocstream & os, row_type row, string const lang) const
2037 {
2038         // we output bottomlines of row r and the toplines of row r+1
2039         // if the latter do not span the whole tabular
2040
2041         // get the bottomlines of row r, and toplines in next row
2042         bool lastrow = row == nrows() - 1;
2043         vector<bool> bottomline, topline;
2044         bool nextrowset = true;
2045         for (col_type c = 0; c < ncols(); ++c) {
2046                 bottomline.push_back(bottomLine(cellIndex(row, c)));
2047                 topline.push_back(!lastrow && topLine(cellIndex(row + 1, c)));
2048                 // If cell is part of a multirow and not the last or first cell of the
2049                 // multirow, no line must be drawn.
2050                 if (!lastrow)
2051                         if (isMultiRow(cellIndex(row, c))
2052                                 && isMultiRow(cellIndex(row + 1, c))) {
2053                                         bottomline[c] = false;
2054                                         topline[c] = false;
2055                                 }
2056                 nextrowset &= topline[c];
2057         }
2058
2059         // combine this row's bottom lines and next row's toplines if necessary
2060         col_type nset = 0;
2061         for (col_type c = 0; c < ncols(); ++c) {
2062                 if (!nextrowset)
2063                         bottomline[c] = bottomline[c] || topline[c];
2064                 if (bottomline[c])
2065                         ++nset;
2066         }
2067
2068         // do nothing if empty, OR incomplete row line with a topline in next row
2069         if (nset == 0 || (nextrowset && nset != ncols()))
2070                 return 0;
2071
2072         if (nset == ncols()) {
2073                 if (use_booktabs)
2074                         os << (lastrow ? "\\bottomrule" : "\\midrule");
2075                 else
2076                         os << "\\hline ";
2077         } else {
2078                 for (col_type c = 0; c < ncols(); ++c) {
2079                         if (bottomline[c]) {
2080                                 col_type offset = 0;
2081                                 for (col_type j = 0 ; j < c; ++j)
2082                                         if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
2083                                                 ++offset;
2084                                 
2085                                 //babel makes the "-" character an active one, so we have to suppress this here
2086                                 //see http://groups.google.com/group/comp.text.tex/browse_thread/thread/af769424a4a0f289#
2087                                 if (lang == "slovak" || lang == "czech")
2088                                         os << "\\expandafter" << (use_booktabs ? "\\cmidrule" : "\\cline")
2089                                         << "\\expandafter{\\expandafter" << c + 1 + offset << "\\string-";
2090                                 else
2091                                         os << (use_booktabs ? "\\cmidrule{" : "\\cline{") << c + 1 + offset << '-';
2092                                 
2093                                 col_type cstart = c;
2094                                 for ( ; c < ncols() && bottomline[c]; ++c) {}
2095                                 
2096                                 for (col_type j = cstart ; j < c ; ++j)
2097                                         if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
2098                                                 ++offset;
2099                                 
2100                                 os << c + offset << "} ";
2101                         }
2102                 }
2103         }
2104         os << "\n";
2105         return 1;
2106 }
2107
2108
2109 int Tabular::TeXCellPreamble(odocstream & os, idx_type cell,
2110                                                          bool & ismulticol, bool & ismultirow) const
2111 {
2112         int ret = 0;
2113         row_type const r = cellRow(cell);
2114         if (is_long_tabular && row_info[r].caption)
2115                 return ret;
2116
2117         Tabular::VAlignment valign =  getVAlignment(cell, !isMultiColumn(cell));
2118         LyXAlignment align = getAlignment(cell, !isMultiColumn(cell));
2119         // figure out how to set the lines
2120         // we always set double lines to the right of the cell
2121         col_type const c = cellColumn(cell);
2122         col_type const nextcol = c + columnSpan(cell);
2123         bool colright = columnRightLine(c);
2124         bool colleft = columnLeftLine(c);
2125         bool nextcolleft = nextcol < ncols() && columnLeftLine(nextcol);
2126         bool nextcellleft = nextcol < ncols() 
2127                 && leftLine(cellIndex(r, nextcol));
2128         bool coldouble = colright && nextcolleft;
2129         bool celldouble = rightLine(cell) && nextcellleft;
2130
2131         ismulticol = isMultiColumn(cell)
2132                 || (c == 0 && colleft != leftLine(cell))
2133                 || ((colright || nextcolleft) && !rightLine(cell) && !nextcellleft)
2134                 || (!colright && !nextcolleft && (rightLine(cell) || nextcellleft))
2135                 || (coldouble != celldouble);
2136
2137         // we center in multicol when no decimal point
2138         ismulticol |= ((column_info[c].alignment == LYX_ALIGN_DECIMAL)
2139                 && (cellInfo(cell).decimal_width == 0));
2140
2141         // up counter by 1 for each decimally aligned col since they use 2 latex cols
2142         int latexcolspan = columnSpan(cell);
2143         for(col_type col = c; col < c + columnSpan(cell); ++col)
2144                 if (column_info[col].alignment == LYX_ALIGN_DECIMAL)
2145                         ++latexcolspan;
2146
2147         if (ismulticol) {
2148                 os << "\\multicolumn{" << latexcolspan << "}{";
2149                 if (c ==0 && leftLine(cell))
2150                         os << '|';
2151                 if (!cellInfo(cell).align_special.empty()) {
2152                         os << cellInfo(cell).align_special;
2153                 } else {
2154                         if (!getPWidth(cell).zero()) {
2155                                 switch (align) {
2156                                 case LYX_ALIGN_LEFT:
2157                                         os << ">{\\raggedright}";
2158                                         break;
2159                                 case LYX_ALIGN_RIGHT:
2160                                         os << ">{\\raggedleft}";
2161                                         break;
2162                                 case LYX_ALIGN_CENTER:
2163                                         os << ">{\\centering}";
2164                                         break;
2165                                 default:
2166                                         break;
2167                                 }
2168                                 switch (valign) {
2169                                 case LYX_VALIGN_TOP:
2170                                         os << 'p';
2171                                         break;
2172                                 case LYX_VALIGN_MIDDLE:
2173                                         os << 'm';
2174                                         break;
2175                                 case LYX_VALIGN_BOTTOM:
2176                                         os << 'b';
2177                                         break;
2178                                 }
2179                                 os << '{'
2180                                    << from_ascii(getPWidth(cell).asLatexString())
2181                                    << '}';
2182                         } else {
2183                                 switch (align) {
2184                                 case LYX_ALIGN_LEFT:
2185                                         os << 'l';
2186                                         break;
2187                                 case LYX_ALIGN_RIGHT:
2188                                         os << 'r';
2189                                         break;
2190                                 default:
2191                                         os << 'c';
2192                                         break;
2193                                 }
2194                         } // end if else !getPWidth
2195                 } // end if else !cellinfo_of_cell
2196                 if (rightLine(cell) || nextcellleft)
2197                         os << '|';
2198                 if (celldouble)
2199                         // add extra vertical line if we want a double one
2200                         os << '|';
2201                 os << "}{";
2202         } // end if ismulticol
2203
2204         // we only need code for the first multirow cell
2205         ismultirow = isMultiRow(cell);
2206         if (ismultirow) {
2207                 os << "\\multirow{" << rowSpan(cell) << "}{";
2208                 if (!getPWidth(cell).zero())
2209                         os << from_ascii(getPWidth(cell).asLatexString());
2210                 else
2211                         // we need to set a default value
2212                         os << "*";
2213                 os << "}{";
2214         } // end if ismultirow
2215
2216         if (getRotateCell(cell)) {
2217                 os << "\\begin{sideways}\n";
2218                 ++ret;
2219         }
2220         if (getUsebox(cell) == BOX_PARBOX) {
2221                 os << "\\parbox[";
2222                 switch (valign) {
2223                 case LYX_VALIGN_TOP:
2224                         os << 't';
2225                         break;
2226                 case LYX_VALIGN_MIDDLE:
2227                         os << 'c';
2228                         break;
2229                 case LYX_VALIGN_BOTTOM:
2230                         os << 'b';
2231                         break;
2232                 }
2233                 os << "]{" << from_ascii(getPWidth(cell).asLatexString())
2234                    << "}{";
2235         } else if (getUsebox(cell) == BOX_MINIPAGE) {
2236                 os << "\\begin{minipage}[";
2237                 switch (valign) {
2238                 case LYX_VALIGN_TOP:
2239                         os << 't';
2240                         break;
2241                 case LYX_VALIGN_MIDDLE:
2242                         os << 'm';
2243                         break;
2244                 case LYX_VALIGN_BOTTOM:
2245                         os << 'b';
2246                         break;
2247                 }
2248                 os << "]{" << from_ascii(getPWidth(cell).asLatexString())
2249                    << "}\n";
2250                 ++ret;
2251         }
2252         return ret;
2253 }
2254
2255
2256 int Tabular::TeXCellPostamble(odocstream & os, idx_type cell,
2257                                                           bool ismulticol, bool ismultirow) const
2258 {
2259         int ret = 0;
2260         row_type const r = cellRow(cell);
2261         if (is_long_tabular && row_info[r].caption)
2262                 return ret;
2263
2264         // usual cells
2265         if (getUsebox(cell) == BOX_PARBOX)
2266                 os << '}';
2267         else if (getUsebox(cell) == BOX_MINIPAGE) {
2268                 os << "%\n\\end{minipage}";
2269                 ret += 2;
2270         }
2271         if (getRotateCell(cell)) {
2272                 os << "%\n\\end{sideways}";
2273                 ++ret;
2274         }
2275         if (ismultirow)
2276                 os << '}';
2277         if (ismulticol)
2278                 os << '}';
2279
2280         return ret;
2281 }
2282
2283
2284 int Tabular::TeXLongtableHeaderFooter(odocstream & os,
2285                                          OutputParams const & runparams) const
2286 {
2287         if (!is_long_tabular)
2288                 return 0;
2289
2290         int ret = 0;
2291         // caption handling
2292         // the caption must be output before the headers
2293         if (haveLTCaption()) {
2294                 for (row_type r = 0; r < nrows(); ++r) {
2295                         if (row_info[r].caption) {
2296                                 ret += TeXRow(os, r, runparams);
2297                         }
2298                 }
2299         }
2300         // output first header info
2301         // first header must be output before the header, otherwise the
2302         // correct caption placement becomes really weird
2303         if (haveLTFirstHead()) {
2304                 if (endfirsthead.topDL) {
2305                         os << "\\hline\n";
2306                         ++ret;
2307                 }
2308                 for (row_type r = 0; r < nrows(); ++r) {
2309                         if (row_info[r].endfirsthead) {
2310                                 ret += TeXRow(os, r, runparams);
2311                         }
2312                 }
2313                 if (endfirsthead.bottomDL) {
2314                         os << "\\hline\n";
2315                         ++ret;
2316                 }
2317                 os << "\\endfirsthead\n";
2318                 ++ret;
2319         }
2320         // output header info
2321         if (haveLTHead()) {
2322                 if (endfirsthead.empty && !haveLTFirstHead()) {
2323                         os << "\\endfirsthead\n";
2324                         ++ret;
2325                 }
2326                 if (endhead.topDL) {
2327                         os << "\\hline\n";
2328                         ++ret;
2329                 }
2330                 for (row_type r = 0; r < nrows(); ++r) {
2331                         if (row_info[r].endhead) {
2332                                 ret += TeXRow(os, r, runparams);
2333                         }
2334                 }
2335                 if (endhead.bottomDL) {
2336                         os << "\\hline\n";
2337                         ++ret;
2338                 }
2339                 os << "\\endhead\n";
2340                 ++ret;
2341         }
2342         // output footer info
2343         if (haveLTFoot()) {
2344                 if (endfoot.topDL) {
2345                         os << "\\hline\n";
2346                         ++ret;
2347                 }
2348                 for (row_type r = 0; r < nrows(); ++r) {
2349                         if (row_info[r].endfoot) {
2350                                 ret += TeXRow(os, r, runparams);
2351                         }
2352                 }
2353                 if (endfoot.bottomDL) {
2354                         os << "\\hline\n";
2355                         ++ret;
2356                 }
2357                 os << "\\endfoot\n";
2358                 ++ret;
2359                 if (endlastfoot.empty && !haveLTLastFoot()) {
2360                         os << "\\endlastfoot\n";
2361                         ++ret;
2362                 }
2363         }
2364         // output lastfooter info
2365         if (haveLTLastFoot()) {
2366                 if (endlastfoot.topDL) {
2367                         os << "\\hline\n";
2368                         ++ret;
2369                 }
2370                 for (row_type r = 0; r < nrows(); ++r) {
2371                         if (row_info[r].endlastfoot) {
2372                                 ret += TeXRow(os, r, runparams);
2373                         }
2374                 }
2375                 if (endlastfoot.bottomDL) {
2376                         os << "\\hline\n";
2377                         ++ret;
2378                 }
2379                 os << "\\endlastfoot\n";
2380                 ++ret;
2381         }
2382         return ret;
2383 }
2384
2385
2386 bool Tabular::isValidRow(row_type row) const
2387 {
2388         if (!is_long_tabular)
2389                 return true;
2390         return !row_info[row].endhead && !row_info[row].endfirsthead
2391                 && !row_info[row].endfoot && !row_info[row].endlastfoot
2392                 && !row_info[row].caption;
2393 }
2394
2395
2396 int Tabular::TeXRow(odocstream & os, row_type row,
2397                        OutputParams const & runparams) const
2398 {
2399         idx_type cell = cellIndex(row, 0);
2400         shared_ptr<InsetTableCell> inset = cellInset(cell);
2401         Paragraph const & par = inset->paragraphs().front();
2402         string const lang = par.getParLanguage(buffer().params())->lang();
2403
2404         //output the top line
2405         int ret = TeXTopHLine(os, row, lang);
2406
2407         if (row_info[row].top_space_default) {
2408                 if (use_booktabs)
2409                         os << "\\addlinespace\n";
2410                 else
2411                         os << "\\noalign{\\vskip\\doublerulesep}\n";
2412                 ++ret;
2413         } else if(!row_info[row].top_space.zero()) {
2414                 if (use_booktabs)
2415                         os << "\\addlinespace["
2416                            << from_ascii(row_info[row].top_space.asLatexString())
2417                            << "]\n";
2418                 else {
2419                         os << "\\noalign{\\vskip"
2420                            << from_ascii(row_info[row].top_space.asLatexString())
2421                            << "}\n";
2422                 }
2423                 ++ret;
2424         }
2425         bool ismulticol = false;
2426         bool ismultirow = false;
2427         for (col_type c = 0; c < ncols(); ++c) {
2428                 if (isPartOfMultiColumn(row, c))
2429                         continue;
2430                         
2431                 if (isPartOfMultiRow(row, c) && 
2432                         column_info[c].alignment != LYX_ALIGN_DECIMAL) {
2433                         os << " & "; 
2434                         continue;
2435                 }
2436
2437                 cell = cellIndex(row, c);
2438                 ret += TeXCellPreamble(os, cell, ismulticol, ismultirow);
2439                 shared_ptr<InsetTableCell> inset = cellInset(cell);
2440
2441                 Paragraph const & par = inset->paragraphs().front();
2442                 bool rtl = par.isRTL(buffer().params())
2443                         && !par.empty()
2444                         && getPWidth(cell).zero();
2445
2446                 if (rtl) {
2447                         string const lang =
2448                                 par.getParLanguage(buffer().params())->lang();
2449                         if (lang == "farsi")
2450                                 os << "\\textFR{";
2451                         else if (lang == "arabic_arabi")
2452                                 os << "\\textAR{";
2453                         // currently, remaning RTL languages are
2454                         // arabic_arabtex and hebrew
2455                         else
2456                                 os << "\\R{";
2457                 }
2458                 // pass to the OutputParams that we are in a cell and
2459                 // which alignment we have set.
2460                 // InsetNewline needs this context information.
2461                 OutputParams newrp(runparams);
2462                 newrp.inTableCell = (getAlignment(cell) == LYX_ALIGN_BLOCK)
2463                                     ? OutputParams::PLAIN
2464                                     : OutputParams::ALIGNED;
2465
2466                 if (getAlignment(cell) == LYX_ALIGN_DECIMAL
2467                         && cellInfo(cell).decimal_width != 0) {
2468                         // copy cell and split in 2
2469                         InsetTableCell head = InsetTableCell(*cellInset(cell).get());
2470                         head.getText(0)->setMacrocontextPosition(
2471                                 cellInset(cell)->getText(0)->macrocontextPosition());
2472                         head.setBuffer(buffer());
2473                         bool hassep = false;
2474                         InsetTableCell tail = splitCell(head, column_info[c].decimal_point, hassep);
2475                         head.latex(os, newrp);
2476                         os << '&';
2477                         ret += tail.latex(os, newrp);
2478                 } else if (!isPartOfMultiRow(row, c))
2479                         ret += inset->latex(os, newrp);
2480
2481                 runparams.encoding = newrp.encoding;
2482                 if (rtl)
2483                         os << '}';
2484
2485                 ret += TeXCellPostamble(os, cell, ismulticol, ismultirow);
2486                 if (cell != getLastCellInRow(row)) { // not last cell in row
2487                         os << " & ";
2488                 }
2489         }
2490         if (row_info[row].caption && !endfirsthead.empty && !haveLTFirstHead())
2491                 // if no first header and no empty first header is used,
2492                 // the caption needs to be terminated by \endfirsthead
2493                 // (bug 6057)
2494                 os << "\\endfirsthead";
2495         else
2496                 os << "\\tabularnewline";
2497         if (row_info[row].bottom_space_default) {
2498                 if (use_booktabs)
2499                         os << "\\addlinespace";
2500                 else
2501                         os << "[\\doublerulesep]";
2502         } else if (!row_info[row].bottom_space.zero()) {
2503                 if (use_booktabs)
2504                         os << "\\addlinespace";
2505                 os << '['
2506                    << from_ascii(row_info[row].bottom_space.asLatexString())
2507                    << ']';
2508         }
2509         os << '\n';
2510         ++ret;
2511
2512         //output the bottom line
2513         ret += TeXBottomHLine(os, row, lang);
2514
2515         if (row_info[row].interline_space_default) {
2516                 if (use_booktabs)
2517                         os << "\\addlinespace\n";
2518                 else
2519                         os << "\\noalign{\\vskip\\doublerulesep}\n";
2520                 ++ret;
2521         } else if (!row_info[row].interline_space.zero()) {
2522                 if (use_booktabs)
2523                         os << "\\addlinespace["
2524                            << from_ascii(row_info[row].interline_space.asLatexString())
2525                            << "]\n";
2526                 else
2527                         os << "\\noalign{\\vskip"
2528                            << from_ascii(row_info[row].interline_space.asLatexString())
2529                            << "}\n";
2530                 ++ret;
2531         }
2532         return ret;
2533 }
2534
2535
2536 int Tabular::latex(odocstream & os, OutputParams const & runparams) const
2537 {
2538         int ret = 0;
2539
2540         //+---------------------------------------------------------------------
2541         //+                      first the opening preamble                    +
2542         //+---------------------------------------------------------------------
2543
2544         if (rotate) {
2545                 os << "\\begin{sideways}\n";
2546                 ++ret;
2547         }
2548         if (is_long_tabular) {
2549                 os << "\\begin{longtable}";
2550                 switch (longtabular_alignment) {
2551                 case LYX_LONGTABULAR_ALIGN_LEFT:
2552                         os << "[l]";
2553                         break;
2554                 case LYX_LONGTABULAR_ALIGN_CENTER:
2555                         break;
2556                 case LYX_LONGTABULAR_ALIGN_RIGHT:
2557                         os << "[r]";
2558                         break;
2559                 }
2560         } else {
2561                 os << "\\begin{tabular}";
2562                 switch (tabular_valignment) {
2563                 case LYX_VALIGN_TOP:
2564                         os << "[t]";
2565                         break;
2566                 case LYX_VALIGN_MIDDLE:
2567                         break;
2568                 case LYX_VALIGN_BOTTOM:
2569                         os << "[b]";
2570                         break;
2571                 }
2572         }
2573         
2574         os << "{";
2575
2576         for (col_type c = 0; c < ncols(); ++c) {
2577                 if (columnLeftLine(c))
2578                         os << '|';
2579                 if (!column_info[c].align_special.empty()) {
2580                         os << column_info[c].align_special;
2581                 } else {
2582                         if (!column_info[c].p_width.zero()) {
2583                                 switch (column_info[c].alignment) {
2584                                 case LYX_ALIGN_LEFT:
2585                                         os << ">{\\raggedright}";
2586                                         break;
2587                                 case LYX_ALIGN_RIGHT:
2588                                         os << ">{\\raggedleft}";
2589                                         break;
2590                                 case LYX_ALIGN_CENTER:
2591                                         os << ">{\\centering}";
2592                                         break;
2593                                 case LYX_ALIGN_NONE:
2594                                 case LYX_ALIGN_BLOCK:
2595                                 case LYX_ALIGN_LAYOUT:
2596                                 case LYX_ALIGN_SPECIAL:
2597                                 case LYX_ALIGN_DECIMAL:
2598                                         break;
2599                                 }
2600
2601                                 switch (column_info[c].valignment) {
2602                                 case LYX_VALIGN_TOP:
2603                                         os << 'p';
2604                                         break;
2605                                 case LYX_VALIGN_MIDDLE:
2606                                         os << 'm';
2607                                         break;
2608                                 case LYX_VALIGN_BOTTOM:
2609                                         os << 'b';
2610                                         break;
2611                         }
2612                                 os << '{'
2613                                    << from_ascii(column_info[c].p_width.asLatexString())
2614                                    << '}';
2615                         } else {
2616                                 switch (column_info[c].alignment) {
2617                                 case LYX_ALIGN_LEFT:
2618                                         os << 'l';
2619                                         break;
2620                                 case LYX_ALIGN_RIGHT:
2621                                         os << 'r';
2622                                         break;
2623                                 case LYX_ALIGN_DECIMAL:
2624                                         os << "r@{\\extracolsep{0pt}" << column_info[c].decimal_point << "}l";
2625                                         break;
2626                                 default:
2627                                         os << 'c';
2628                                         break;
2629                                 }
2630                         } // end if else !column_info[i].p_width
2631                 } // end if else !column_info[i].align_special
2632                 if (columnRightLine(c))
2633                         os << '|';
2634         }
2635         os << "}\n";
2636         ++ret;
2637
2638         ret += TeXLongtableHeaderFooter(os, runparams);
2639
2640         //+---------------------------------------------------------------------
2641         //+                      the single row and columns (cells)            +
2642         //+---------------------------------------------------------------------
2643
2644         for (row_type r = 0; r < nrows(); ++r) {
2645                 if (isValidRow(r)) {
2646                         ret += TeXRow(os, r, runparams);
2647                         if (is_long_tabular && row_info[r].newpage) {
2648                                 os << "\\newpage\n";
2649                                 ++ret;
2650                         }
2651                 }
2652         }
2653
2654         //+---------------------------------------------------------------------
2655         //+                      the closing of the tabular                    +
2656         //+---------------------------------------------------------------------
2657
2658         if (is_long_tabular)
2659                 os << "\\end{longtable}";
2660         else
2661                 os << "\\end{tabular}";
2662         if (rotate) {
2663                 os << "\n\\end{sideways}";
2664                 ++ret;
2665         }
2666
2667         return ret;
2668 }
2669
2670
2671 int Tabular::docbookRow(odocstream & os, row_type row,
2672                            OutputParams const & runparams) const
2673 {
2674         int ret = 0;
2675         idx_type cell = getFirstCellInRow(row);
2676
2677         os << "<row>\n";
2678         for (col_type c = 0; c < ncols(); ++c) {
2679                 if (isPartOfMultiColumn(row, c))
2680                         continue;
2681
2682                 os << "<entry align=\"";
2683                 switch (getAlignment(cell)) {
2684                 case LYX_ALIGN_LEFT:
2685                         os << "left";
2686                         break;
2687                 case LYX_ALIGN_RIGHT:
2688                         os << "right";
2689                         break;
2690                 default:
2691                         os << "center";
2692                         break;
2693                 }
2694
2695                 os << "\" valign=\"";
2696                 switch (getVAlignment(cell)) {
2697                 case LYX_VALIGN_TOP:
2698                         os << "top";
2699                         break;
2700                 case LYX_VALIGN_BOTTOM:
2701                         os << "bottom";
2702                         break;
2703                 case LYX_VALIGN_MIDDLE:
2704                         os << "middle";
2705                 }
2706                 os << '"';
2707
2708                 if (isMultiColumn(cell)) {
2709                         os << " namest=\"col" << c << "\" ";
2710                         os << "nameend=\"col" << c + columnSpan(cell) - 1 << '"';
2711                 }
2712
2713                 os << '>';
2714                 ret += cellInset(cell)->docbook(os, runparams);
2715                 os << "</entry>\n";
2716                 ++cell;
2717         }
2718         os << "</row>\n";
2719         return ret;
2720 }
2721
2722
2723 int Tabular::docbook(odocstream & os, OutputParams const & runparams) const
2724 {
2725         int ret = 0;
2726
2727         //+---------------------------------------------------------------------
2728         //+                      first the opening preamble                    +
2729         //+---------------------------------------------------------------------
2730
2731         os << "<tgroup cols=\"" << ncols()
2732            << "\" colsep=\"1\" rowsep=\"1\">\n";
2733
2734         for (col_type c = 0; c < ncols(); ++c) {
2735                 os << "<colspec colname=\"col" << c << "\" align=\"";
2736                 switch (column_info[c].alignment) {
2737                 case LYX_ALIGN_LEFT:
2738                         os << "left";
2739                         break;
2740                 case LYX_ALIGN_RIGHT:
2741                         os << "right";
2742                         break;
2743                 default:
2744                         os << "center";
2745                         break;
2746                 }
2747                 os << '"';
2748                 if (runparams.flavor == OutputParams::XML)
2749                         os << '/';
2750                 os << ">\n";
2751                 ++ret;
2752         }
2753
2754         //+---------------------------------------------------------------------
2755         //+                      Long Tabular case                             +
2756         //+---------------------------------------------------------------------
2757
2758         // output caption info
2759         if (haveLTCaption()) {
2760                 os << "<caption>\n";
2761                 ++ret;
2762                 for (row_type r = 0; r < nrows(); ++r) {
2763                         if (row_info[r].caption) {
2764                                 ret += docbookRow(os, r, runparams);
2765                         }
2766                 }
2767                 os << "</caption>\n";
2768                 ++ret;
2769         }
2770         // output header info
2771         if (haveLTHead() || haveLTFirstHead()) {
2772                 os << "<thead>\n";
2773                 ++ret;
2774                 for (row_type r = 0; r < nrows(); ++r) {
2775                         if (row_info[r].endhead || row_info[r].endfirsthead) {
2776                                 ret += docbookRow(os, r, runparams);
2777                         }
2778                 }
2779                 os << "</thead>\n";
2780                 ++ret;
2781         }
2782         // output footer info
2783         if (haveLTFoot() || haveLTLastFoot()) {
2784                 os << "<tfoot>\n";
2785                 ++ret;
2786                 for (row_type r = 0; r < nrows(); ++r) {
2787                         if (row_info[r].endfoot || row_info[r].endlastfoot) {
2788                                 ret += docbookRow(os, r, runparams);
2789                         }
2790                 }
2791                 os << "</tfoot>\n";
2792                 ++ret;
2793         }
2794
2795         //+---------------------------------------------------------------------
2796         //+                      the single row and columns (cells)            +
2797         //+---------------------------------------------------------------------
2798
2799         os << "<tbody>\n";
2800         ++ret;
2801         for (row_type r = 0; r < nrows(); ++r) {
2802                 if (isValidRow(r)) {
2803                         ret += docbookRow(os, r, runparams);
2804                 }
2805         }
2806         os << "</tbody>\n";
2807         ++ret;
2808         //+---------------------------------------------------------------------
2809         //+                      the closing of the tabular                    +
2810         //+---------------------------------------------------------------------
2811
2812         os << "</tgroup>";
2813         ++ret;
2814
2815         return ret;
2816 }
2817
2818
2819 docstring Tabular::xhtmlRow(XHTMLStream & xs, row_type row,
2820                            OutputParams const & runparams) const
2821 {
2822         docstring ret;
2823         idx_type cell = getFirstCellInRow(row);
2824
2825         xs << html::StartTag("tr");
2826         for (col_type c = 0; c < ncols(); ++c) {
2827                 if (isPartOfMultiColumn(row, c))
2828                         continue;
2829
2830                 stringstream attr;
2831                 attr << "align='";
2832                 switch (getAlignment(cell)) {
2833                 case LYX_ALIGN_LEFT:
2834                         attr << "left";
2835                         break;
2836                 case LYX_ALIGN_RIGHT:
2837                         attr << "right";
2838                         break;
2839                 default:
2840                         attr << "center";
2841                         break;
2842                 }
2843                 attr << "'";
2844                 attr << " valign='";
2845                 switch (getVAlignment(cell)) {
2846                 case LYX_VALIGN_TOP:
2847                         attr << "top";
2848                         break;
2849                 case LYX_VALIGN_BOTTOM:
2850                         attr << "bottom";
2851                         break;
2852                 case LYX_VALIGN_MIDDLE:
2853                         attr << "middle";
2854                 }
2855                 attr << "'";
2856
2857                 if (isMultiColumn(cell))
2858                         attr << " colspan='" << columnSpan(cell) << "'";
2859
2860                 xs << html::StartTag("td", attr.str());
2861                 ret += cellInset(cell)->xhtml(xs, runparams);
2862                 xs << html::EndTag("td");
2863                 ++cell;
2864         }
2865         xs << html::EndTag("tr");
2866         return ret;
2867 }
2868
2869
2870 docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
2871 {
2872         docstring ret;
2873         // It's unclear to me if we need to mess with the long table stuff. 
2874         // We can borrow that too from docbook, if so.
2875
2876         xs << html::StartTag("tbody");
2877         for (row_type r = 0; r < nrows(); ++r) {
2878                 if (isValidRow(r)) {
2879                         ret += xhtmlRow(xs, r, runparams);
2880                 }
2881         }
2882         xs << html::EndTag("tbody");
2883         return ret;
2884 }
2885
2886
2887 bool Tabular::plaintextTopHLine(odocstream & os, row_type row,
2888                                    vector<unsigned int> const & clen) const
2889 {
2890         idx_type const fcell = getFirstCellInRow(row);
2891         idx_type const n = numberOfCellsInRow(row) + fcell;
2892         idx_type tmp = 0;
2893
2894         for (idx_type i = fcell; i < n; ++i) {
2895                 if (topLine(i)) {
2896                         ++tmp;
2897                         break;
2898                 }
2899         }
2900         if (!tmp)
2901                 return false;
2902
2903         char_type ch;
2904         for (idx_type i = fcell; i < n; ++i) {
2905                 if (topLine(i)) {
2906                         if (leftLine(i))
2907                                 os << "+-";
2908                         else
2909                                 os << "--";
2910                         ch = '-';
2911                 } else {
2912                         os << "  ";
2913                         ch = ' ';
2914                 }
2915                 col_type column = cellColumn(i);
2916                 int len = clen[column];
2917                 while (column < ncols() - 1
2918                        && isPartOfMultiColumn(row, ++column))
2919                         len += clen[column] + 4;
2920                 os << docstring(len, ch);
2921                 if (topLine(i)) {
2922                         if (rightLine(i))
2923                                 os << "-+";
2924                         else
2925                                 os << "--";
2926                 } else {
2927                         os << "  ";
2928                 }
2929         }
2930         os << endl;
2931         return true;
2932 }
2933
2934
2935 bool Tabular::plaintextBottomHLine(odocstream & os, row_type row,
2936                                       vector<unsigned int> const & clen) const
2937 {
2938         idx_type const fcell = getFirstCellInRow(row);
2939         idx_type const n = numberOfCellsInRow(row) + fcell;
2940         idx_type tmp = 0;
2941
2942         for (idx_type i = fcell; i < n; ++i) {
2943                 if (bottomLine(i)) {
2944                         ++tmp;
2945                         break;
2946                 }
2947         }
2948         if (!tmp)
2949                 return false;
2950
2951         char_type ch;
2952         for (idx_type i = fcell; i < n; ++i) {
2953                 if (bottomLine(i)) {
2954                         if (leftLine(i))
2955                                 os << "+-";
2956                         else
2957                                 os << "--";
2958                         ch = '-';
2959                 } else {
2960                         os << "  ";
2961                         ch = ' ';
2962                 }
2963                 col_type column = cellColumn(i);
2964                 int len = clen[column];
2965                 while (column < ncols() - 1 && isPartOfMultiColumn(row, ++column))
2966                         len += clen[column] + 4;
2967                 os << docstring(len, ch);
2968                 if (bottomLine(i)) {
2969                         if (rightLine(i))
2970                                 os << "-+";
2971                         else
2972                                 os << "--";
2973                 } else {
2974                         os << "  ";
2975                 }
2976         }
2977         os << endl;
2978         return true;
2979 }
2980
2981
2982 void Tabular::plaintextPrintCell(odocstream & os,
2983                                OutputParams const & runparams,
2984                                idx_type cell, row_type row, col_type column,
2985                                vector<unsigned int> const & clen,
2986                                bool onlydata) const
2987 {
2988         odocstringstream sstr;
2989         cellInset(cell)->plaintext(sstr, runparams);
2990
2991         if (onlydata) {
2992                 os << sstr.str();
2993                 return;
2994         }
2995
2996         if (leftLine(cell))
2997                 os << "| ";
2998         else
2999                 os << "  ";
3000
3001         unsigned int len1 = sstr.str().length();
3002         unsigned int len2 = clen[column];
3003         while (column < ncols() - 1 && isPartOfMultiColumn(row, ++column))
3004                 len2 += clen[column] + 4;
3005         len2 -= len1;
3006
3007         switch (getAlignment(cell)) {
3008         default:
3009         case LYX_ALIGN_LEFT:
3010                 len1 = 0;
3011                 break;
3012         case LYX_ALIGN_RIGHT:
3013                 len1 = len2;
3014                 len2 = 0;
3015                 break;
3016         case LYX_ALIGN_CENTER:
3017                 len1 = len2 / 2;
3018                 len2 -= len1;
3019                 break;
3020         }
3021
3022         os << docstring(len1, ' ') << sstr.str()
3023            << docstring(len2, ' ');
3024
3025         if (rightLine(cell))
3026                 os << " |";
3027         else
3028                 os << "  ";
3029 }
3030
3031
3032 void Tabular::plaintext(odocstream & os,
3033                            OutputParams const & runparams, int const depth,
3034                            bool onlydata, char_type delim) const
3035 {
3036         // first calculate the width of the single columns
3037         vector<unsigned int> clen(ncols());
3038
3039         if (!onlydata) {
3040                 // first all non multicolumn cells!
3041                 for (col_type c = 0; c < ncols(); ++c) {
3042                         clen[c] = 0;
3043                         for (row_type r = 0; r < nrows(); ++r) {
3044                                 idx_type cell = cellIndex(r, c);
3045                                 if (isMultiColumn(cell))
3046                                         continue;
3047                                 odocstringstream sstr;
3048                                 cellInset(cell)->plaintext(sstr, runparams);
3049                                 if (clen[c] < sstr.str().length())
3050                                         clen[c] = sstr.str().length();
3051                         }
3052                 }
3053                 // then all multicolumn cells!
3054                 for (col_type c = 0; c < ncols(); ++c) {
3055                         for (row_type r = 0; r < nrows(); ++r) {
3056                                 idx_type cell = cellIndex(r, c);
3057                                 if (cell_info[r][c].multicolumn != CELL_BEGIN_OF_MULTICOLUMN)
3058                                         continue;
3059                                 odocstringstream sstr;
3060                                 cellInset(cell)->plaintext(sstr, runparams);
3061                                 int len = int(sstr.str().length());
3062                                 idx_type const n = columnSpan(cell);
3063                                 for (col_type k = c; len > 0 && k < c + n - 1; ++k)
3064                                         len -= clen[k];
3065                                 if (len > int(clen[c + n - 1]))
3066                                         clen[c + n - 1] = len;
3067                         }
3068                 }
3069         }
3070         idx_type cell = 0;
3071         for (row_type r = 0; r < nrows(); ++r) {
3072                 if (!onlydata && plaintextTopHLine(os, r, clen))
3073                         os << docstring(depth * 2, ' ');
3074                 for (col_type c = 0; c < ncols(); ++c) {
3075                         if (isPartOfMultiColumn(r, c))
3076                                 continue;
3077                         if (onlydata && c > 0)
3078                                 // we don't use operator<< for single UCS4 character.
3079                                 // see explanation in docstream.h
3080                                 os.put(delim);
3081                         plaintextPrintCell(os, runparams, cell, r, c, clen, onlydata);
3082                         ++cell;
3083                 }
3084                 os << endl;
3085                 if (!onlydata) {
3086                         os << docstring(depth * 2, ' ');
3087                         if (plaintextBottomHLine(os, r, clen))
3088                                 os << docstring(depth * 2, ' ');
3089                 }
3090         }
3091 }
3092
3093
3094 shared_ptr<InsetTableCell> Tabular::cellInset(idx_type cell) const
3095 {
3096         return cell_info[cellRow(cell)][cellColumn(cell)].inset;
3097 }
3098
3099
3100 shared_ptr<InsetTableCell> Tabular::cellInset(row_type row,
3101                                                col_type column) const
3102 {
3103         return cell_info[row][column].inset;
3104 }
3105
3106
3107 void Tabular::setCellInset(row_type row, col_type column,
3108                               shared_ptr<InsetTableCell> ins) const
3109 {
3110         CellData & cd = cell_info[row][column];
3111         cd.inset = ins;
3112 }
3113
3114
3115 void Tabular::validate(LaTeXFeatures & features) const
3116 {
3117         features.require("NeedTabularnewline");
3118         if (use_booktabs)
3119                 features.require("booktabs");
3120         if (is_long_tabular)
3121                 features.require("longtable");
3122         if (needRotating())
3123                 features.require("rotating");
3124         for (idx_type cell = 0; cell < numberofcells; ++cell) {
3125                 if (isMultiRow(cell))
3126                         features.require("multirow");
3127                 if (getVAlignment(cell) != LYX_VALIGN_TOP
3128                     || !getPWidth(cell).zero())
3129                         features.require("array");
3130                 cellInset(cell)->validate(features);
3131         }
3132 }
3133
3134
3135 Tabular::BoxType Tabular::useParbox(idx_type cell) const
3136 {
3137         ParagraphList const & parlist = cellInset(cell)->paragraphs();
3138         ParagraphList::const_iterator cit = parlist.begin();
3139         ParagraphList::const_iterator end = parlist.end();
3140
3141         for (; cit != end; ++cit)
3142                 for (int i = 0; i < cit->size(); ++i)
3143                         if (cit->isNewline(i))
3144                                 return BOX_PARBOX;
3145
3146         return BOX_NONE;
3147 }
3148
3149
3150 /////////////////////////////////////////////////////////////////////
3151 //
3152 // InsetTableCell
3153 //
3154 /////////////////////////////////////////////////////////////////////
3155
3156 InsetTableCell::InsetTableCell(Buffer * buf)
3157         : InsetText(buf, InsetText::PlainLayout), isFixedWidth(false),
3158           contentAlign(LYX_ALIGN_CENTER)
3159 {}
3160
3161
3162 bool InsetTableCell::forcePlainLayout(idx_type) const
3163 {
3164         return !isFixedWidth;
3165 }
3166
3167
3168 bool InsetTableCell::allowParagraphCustomization(idx_type) const
3169 {
3170         return isFixedWidth;
3171 }
3172
3173
3174 bool InsetTableCell::getStatus(Cursor & cur, FuncRequest const & cmd,
3175         FuncStatus & status) const
3176 {
3177         bool enabled;
3178         switch (cmd.action()) {
3179         case LFUN_LAYOUT:
3180                 enabled = !forcePlainLayout();
3181                 break;
3182         case LFUN_LAYOUT_PARAGRAPH:
3183                 enabled = allowParagraphCustomization();
3184                 break;
3185         default:
3186                 return InsetText::getStatus(cur, cmd, status);
3187         }
3188         status.setEnabled(enabled);
3189         return true;
3190 }
3191
3192 docstring InsetTableCell::asString(bool intoInsets) 
3193 {
3194         docstring retval;
3195         if (paragraphs().empty())
3196                 return retval;
3197         ParagraphList::const_iterator it = paragraphs().begin();
3198         ParagraphList::const_iterator en = paragraphs().end();
3199         bool first = true;
3200         for (; it != en; ++it) {
3201                 if (!first)
3202                         retval += "\n";
3203                 else
3204                         first = false;
3205                 retval += it->asString(intoInsets ? AS_STR_INSETS : AS_STR_NONE);
3206         }
3207         return retval;
3208 }
3209
3210
3211 docstring InsetTableCell::xhtml(XHTMLStream & xs, OutputParams const & rp) const
3212 {
3213         if (!isFixedWidth)
3214                 return InsetText::insetAsXHTML(xs, rp, InsetText::JustText);
3215         return InsetText::xhtml(xs, rp);
3216 }
3217
3218
3219
3220 /////////////////////////////////////////////////////////////////////
3221 //
3222 // InsetTabular
3223 //
3224 /////////////////////////////////////////////////////////////////////
3225
3226 InsetTabular::InsetTabular(Buffer * buf, row_type rows,
3227                            col_type columns)
3228         : Inset(buf), tabular(buf, max(rows, row_type(1)), max(columns, col_type(1))), scx_(0), 
3229         rowselect_(false), colselect_(false)
3230 {
3231 }
3232
3233
3234 InsetTabular::InsetTabular(InsetTabular const & tab)
3235         : Inset(tab), tabular(tab.tabular),  scx_(0)
3236 {
3237 }
3238
3239
3240 InsetTabular::~InsetTabular()
3241 {
3242         hideDialogs("tabular", this);
3243 }
3244
3245
3246 void InsetTabular::setBuffer(Buffer & buf)
3247 {
3248         tabular.setBuffer(buf);
3249         Inset::setBuffer(buf);
3250 }
3251
3252
3253 bool InsetTabular::insetAllowed(InsetCode code) const
3254 {
3255         switch (code) {
3256         case FLOAT_CODE:
3257         case MARGIN_CODE:
3258         case MATHMACRO_CODE:
3259         case WRAP_CODE:
3260                 return false;
3261
3262         case CAPTION_CODE:
3263                 return tabular.is_long_tabular;
3264
3265         default:
3266                 return true;
3267         }
3268 }
3269
3270
3271 void InsetTabular::write(ostream & os) const
3272 {
3273         os << "Tabular" << endl;
3274         tabular.write(os);
3275 }
3276
3277
3278 docstring InsetTabular::contextMenu(BufferView const &, int, int) const
3279 {
3280         // FIXME: depending on the selection state, we could offer a different menu.
3281         return from_ascii("context-tabular");
3282 }
3283
3284
3285 void InsetTabular::read(Lexer & lex)
3286 {
3287         //bool const old_format = (lex.getString() == "\\LyXTable");
3288
3289         tabular.read(lex);
3290
3291         //if (old_format)
3292         //      return;
3293
3294         lex.next();
3295         string token = lex.getString();
3296         while (lex && token != "\\end_inset") {
3297                 lex.next();
3298                 token = lex.getString();
3299         }
3300         if (!lex)
3301                 lex.printError("Missing \\end_inset at this point. ");
3302 }
3303
3304
3305 int InsetTabular::rowFromY(Cursor & cur, int y) const
3306 {
3307         // top y coordinate of tabular
3308         int h = yo(cur.bv()) - tabular.rowAscent(0);
3309         row_type r = 0;
3310         for (; r < tabular.nrows() && y > h; ++r)
3311                 h += tabular.rowAscent(r) + tabular.rowDescent(r)
3312                 + tabular.interRowSpace(r);
3313
3314         return r - 1;
3315 }
3316
3317
3318 int InsetTabular::columnFromX(Cursor & cur, int x) const
3319 {
3320         // left x coordinate of tabular
3321         int w = xo(cur.bv()) + ADD_TO_TABULAR_WIDTH;
3322         col_type c = 0;
3323         for (; c < tabular.ncols() && x > w; ++c)
3324                 w += tabular.cellWidth(c);
3325         return c - 1;
3326 }
3327
3328
3329 void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
3330 {
3331         //lyxerr << "InsetTabular::metrics: " << mi.base.bv << " width: " <<
3332         //      mi.base.textwidth << "\n";
3333         if (!mi.base.bv) {
3334                 LYXERR0("need bv");
3335                 LASSERT(false, /**/);
3336         }
3337
3338         for (row_type r = 0; r < tabular.nrows(); ++r) {
3339                 int maxasc = 0;
3340                 int maxdes = 0;
3341                 for (col_type c = 0; c < tabular.ncols(); ++c) {
3342                         if (tabular.isPartOfMultiColumn(r, c)
3343                                 || tabular.isPartOfMultiRow(r, c))
3344                                 // multicolumn or multirow cell, but not first one
3345                                 continue;
3346                         idx_type const cell = tabular.cellIndex(r, c);
3347                         Dimension dim;
3348                         MetricsInfo m = mi;
3349                         Length const p_width = tabular.getPWidth(cell);
3350                         if (!p_width.zero())
3351                                 m.base.textwidth = p_width.inPixels(mi.base.textwidth);
3352                         tabular.cellInset(cell)->metrics(m, dim);
3353                         if (!p_width.zero())
3354                                 dim.wid = m.base.textwidth;
3355                         tabular.cellInfo(cell).width = dim.wid + 2 * WIDTH_OF_LINE 
3356                                 + tabular.interColumnSpace(cell);
3357
3358                         // FIXME(?): do we need a second metrics call?
3359                         TextMetrics const & tm = 
3360                                 mi.base.bv->textMetrics(tabular.cellInset(cell)->getText(0));
3361
3362                         // determine horiz offset because of decimal align (if necessary)
3363                         int decimal_hoffset = 0;
3364                         int decimal_width = 0;
3365                         if (tabular.getAlignment(cell) == LYX_ALIGN_DECIMAL) {
3366                                 // make a copy which we will split in 2
3367                                 InsetTableCell head = InsetTableCell(*tabular.cellInset(cell).get());
3368                                 head.getText(0)->setMacrocontextPosition(
3369                                         tabular.cellInset(cell)->getText(0)->macrocontextPosition());
3370                                 head.setBuffer(tabular.buffer());
3371                                 // split in 2 and calculate width of each part
3372                                 bool hassep = false;
3373                                 InsetTableCell tail = 
3374                                         splitCell(head, tabular.column_info[c].decimal_point, hassep);
3375                                 Dimension dim1;
3376                                 head.metrics(m, dim1);
3377                                 decimal_hoffset = dim1.width();
3378                                 if (hassep) {
3379                                         tail.metrics(m, dim1);
3380                                         decimal_width = dim1.width();
3381                                 }
3382                         }
3383                         tabular.cell_info[r][c].decimal_hoffset = decimal_hoffset;
3384                         tabular.cell_info[r][c].decimal_width = decimal_width;
3385
3386                         // with LYX_VALIGN_BOTTOM the descent is relative to the last par
3387                         // = descent of text in last par + TEXT_TO_INSET_OFFSET:
3388                         int const lastpardes = tm.last().second->descent()
3389                                 + TEXT_TO_INSET_OFFSET;
3390                         int offset = 0;
3391                         switch (tabular.getVAlignment(cell)) { 
3392                                 case Tabular::LYX_VALIGN_TOP:
3393                                         break; 
3394                                 case Tabular::LYX_VALIGN_MIDDLE:
3395                                         offset = -(dim.des - lastpardes)/2; 
3396                                         break; 
3397                                 case Tabular::LYX_VALIGN_BOTTOM:
3398                                         offset = -(dim.des - lastpardes); 
3399                                         break;
3400                         }
3401                         tabular.cell_info[r][c].voffset = offset;
3402                         maxasc = max(maxasc, dim.asc - offset);
3403                         maxdes = max(maxdes, dim.des + offset);
3404                 }
3405                 int const top_space = tabular.row_info[r].top_space_default ?
3406                         default_line_space :
3407                         tabular.row_info[r].top_space.inPixels(mi.base.textwidth);
3408                 tabular.setRowAscent(r, maxasc + ADD_TO_HEIGHT + top_space);
3409                 int const bottom_space = tabular.row_info[r].bottom_space_default ?
3410                         default_line_space :
3411                         tabular.row_info[r].bottom_space.inPixels(mi.base.textwidth);
3412                 tabular.setRowDescent(r, maxdes + ADD_TO_HEIGHT + bottom_space);
3413         }
3414         tabular.updateColumnWidths();
3415         dim.asc = tabular.rowAscent(0);
3416         dim.des = tabular.height() - dim.asc;
3417         dim.wid = tabular.width() + 2 * ADD_TO_TABULAR_WIDTH;
3418 }
3419
3420
3421 bool InsetTabular::isCellSelected(Cursor & cur, row_type row, col_type col) 
3422         const
3423 {
3424         if (&cur.inset() == this && cur.selection()) {
3425                 if (cur.selIsMultiCell()) {
3426                         row_type rs, re;
3427                         col_type cs, ce;
3428                         getSelection(cur, rs, re, cs, ce);
3429                         
3430                         idx_type const cell = tabular.cellIndex(row, col);
3431                         col_type const cspan = tabular.columnSpan(cell);
3432                         row_type const rspan = tabular.rowSpan(cell);
3433                         if (col + cspan - 1 >= cs && col <= ce 
3434                                 && row + rspan - 1 >= rs && row <= re)
3435                                 return true;
3436                 } else 
3437                         if (col == tabular.cellColumn(cur.idx()) 
3438                                 && row == tabular.cellRow(cur.idx())) {
3439                         CursorSlice const & beg = cur.selBegin();
3440                         CursorSlice const & end = cur.selEnd();
3441
3442                         if ((end.lastpos() > 0 || end.lastpit() > 0)
3443                                   && end.pos() == end.lastpos() && beg.pos() == 0
3444                                   && end.pit() == end.lastpit() && beg.pit() == 0)
3445                                 return true;
3446                 }
3447         }
3448         return false;
3449 }
3450
3451
3452 void InsetTabular::draw(PainterInfo & pi, int x, int y) const
3453 {
3454         x += scx_ + ADD_TO_TABULAR_WIDTH;
3455
3456         BufferView * bv = pi.base.bv;
3457         Cursor & cur = pi.base.bv->cursor();
3458         resetPos(cur);
3459
3460         // FIXME: As the full background is painted in drawSelection(),
3461         // we have no choice but to do a full repaint for the Text cells.
3462         pi.full_repaint = true;
3463
3464         bool const original_selection_state = pi.selected;
3465
3466         idx_type idx = 0;
3467         first_visible_cell = Tabular::npos;
3468         for (row_type r = 0; r < tabular.nrows(); ++r) {
3469                 int nx = x;
3470                 for (col_type c = 0; c < tabular.ncols(); ++c) {
3471                         if (tabular.isPartOfMultiColumn(r, c))
3472                                 continue;
3473                         
3474                         idx = tabular.cellIndex(r, c);
3475                         
3476                         if (tabular.isPartOfMultiRow(r, c)) {
3477                                 nx += tabular.cellWidth(idx);
3478                                 continue;
3479                         }
3480
3481                         if (first_visible_cell == Tabular::npos)
3482                                 first_visible_cell = idx;
3483
3484                         pi.selected |= isCellSelected(cur, r, c);
3485                         int const cx = nx + tabular.textHOffset(idx);
3486                         int const cy = y  + tabular.textVOffset(idx);
3487                         // Cache the Inset position.
3488                         bv->coordCache().insets().add(cell(idx).get(), cx, cy);
3489                         cell(idx)->draw(pi, cx, cy);
3490                         drawCellLines(pi, nx, y, r, idx);
3491                         nx += tabular.cellWidth(idx);
3492                         pi.selected = original_selection_state;
3493                 }
3494
3495                 if (r + 1 < tabular.nrows())
3496                         y += tabular.rowDescent(r) + tabular.rowAscent(r + 1) 
3497                                 + tabular.interRowSpace(r + 1);
3498         }
3499 }
3500
3501
3502 void InsetTabular::drawSelection(PainterInfo & pi, int x, int y) const
3503 {
3504         Cursor & cur = pi.base.bv->cursor();
3505         resetPos(cur);
3506
3507         x += scx_ + ADD_TO_TABULAR_WIDTH;
3508
3509         // FIXME: it is wrong to completely paint the background
3510         // if we want to do single row painting.
3511
3512         // Paint background of current tabular
3513         int const w = tabular.width();
3514         int const h = tabular.height();
3515         int yy = y - tabular.rowAscent(0);
3516         pi.pain.fillRectangle(x, yy, w, h, pi.backgroundColor(this));
3517
3518         if (!cur.selection())
3519                 return;
3520         if (&cur.inset() != this)
3521                 return;
3522
3523         //resetPos(cur);
3524
3525         bool const full_cell_selected = isCellSelected(cur,
3526                 tabular.cellRow(cur.idx()), tabular.cellColumn(cur.idx()));
3527
3528         if (cur.selIsMultiCell() || full_cell_selected) {
3529                 for (row_type r = 0; r < tabular.nrows(); ++r) {
3530                         int xx = x;
3531                         for (col_type c = 0; c < tabular.ncols(); ++c) {
3532                                 if (tabular.isPartOfMultiColumn(r, c))
3533                                         continue;
3534
3535                                 idx_type const cell = tabular.cellIndex(r, c);
3536
3537                                 if (tabular.isPartOfMultiRow(r, c)) {
3538                                         xx += tabular.cellWidth(cell);
3539                                         continue;
3540                                 }
3541                                 int const w = tabular.cellWidth(cell);
3542                                 int const h = tabular.cellHeight(cell);
3543                                 int const yy = y - tabular.rowAscent(r);
3544                                 if (isCellSelected(cur, r, c))
3545                                         pi.pain.fillRectangle(xx, yy, w, h, Color_selection);
3546                                 xx += w;
3547                         }
3548                         if (r + 1 < tabular.nrows())
3549                                 y += tabular.rowDescent(r) + tabular.rowAscent(r + 1)
3550                                      + tabular.interRowSpace(r + 1);
3551                 }
3552
3553         } else {
3554                 x += cellXPos(cur.idx());
3555                 x += tabular.textHOffset(cur.idx());
3556                 cell(cur.idx())->drawSelection(pi, x, 0 /* ignored */);
3557         }
3558 }
3559
3560
3561 void InsetTabular::drawCellLines(PainterInfo & pi, int x, int y,
3562                                  row_type row, idx_type cell) const
3563 {
3564         y -= tabular.rowAscent(row);
3565         int const w = tabular.cellWidth(cell);
3566         int const h = tabular.cellHeight(cell);
3567         Color const linecolor = pi.textColor(Color_tabularline);
3568         Color const gridcolor = pi.textColor(Color_tabularonoffline);
3569
3570         // Top
3571         bool drawline = tabular.topLine(cell)
3572                 || (row > 0 && tabular.bottomLine(tabular.cellAbove(cell)));
3573         pi.pain.line(x, y, x + w, y,
3574                 drawline ? linecolor : gridcolor,
3575                 drawline ? Painter::line_solid : Painter::line_onoffdash);
3576
3577         // Bottom
3578         drawline = tabular.bottomLine(cell);
3579         pi.pain.line(x, y + h, x + w, y + h,
3580                 drawline ? linecolor : gridcolor,
3581                 drawline ? Painter::line_solid : Painter::line_onoffdash);
3582
3583         // Left
3584         col_type const col = tabular.cellColumn(cell);
3585         drawline = tabular.leftLine(cell)
3586                 || (col > 0 && tabular.rightLine(tabular.cellIndex(row, col - 1)));
3587         pi.pain.line(x, y, x, y + h,
3588                 drawline ? linecolor : gridcolor,
3589                 drawline ? Painter::line_solid : Painter::line_onoffdash);
3590
3591         // Right
3592         x -= tabular.interColumnSpace(cell);
3593         drawline = tabular.rightLine(cell)
3594                    || (col + 1 < tabular.ncols()
3595                        && tabular.leftLine(tabular.cellIndex(row, col + 1)));
3596         pi.pain.line(x + w, y, x + w, y + h,
3597                 drawline ? linecolor : gridcolor,
3598                 drawline ? Painter::line_solid : Painter::line_onoffdash);
3599 }
3600
3601
3602 void InsetTabular::edit(Cursor & cur, bool front, EntryDirection)
3603 {
3604         //lyxerr << "InsetTabular::edit: " << this << endl;
3605         cur.finishUndo();
3606         cur.setSelection(false);
3607         cur.push(*this);
3608         if (front) {
3609                 if (isRightToLeft(cur))
3610                         cur.idx() = tabular.getLastCellInRow(0);
3611                 else
3612                         cur.idx() = 0;
3613                 cur.pit() = 0;
3614                 cur.pos() = 0;
3615         } else {
3616                 if (isRightToLeft(cur))
3617                         cur.idx() = tabular.getFirstCellInRow(tabular.nrows() - 1);
3618                 else
3619                         cur.idx() = tabular.numberofcells - 1;
3620                 cur.pit() = 0;
3621                 cur.pos() = cur.lastpos(); // FIXME crude guess
3622         }
3623         cur.setCurrentFont();
3624         // FIXME: this accesses the position cache before it is initialized
3625         //resetPos(cur);
3626         //cur.bv().fitCursor();
3627 }
3628
3629
3630 void InsetTabular::updateBuffer(ParIterator const & it, UpdateType utype)
3631 {
3632         // In a longtable, tell captions what the current float is
3633         Counters & cnts = buffer().masterBuffer()->params().documentClass().counters();
3634         string const saveflt = cnts.current_float();
3635         if (tabular.is_long_tabular)
3636                 cnts.current_float("table");
3637
3638         ParIterator it2 = it;
3639         it2.forwardPos();
3640         size_t const end = it2.nargs();
3641         for ( ; it2.idx() < end; it2.top().forwardIdx())
3642                 buffer().updateBuffer(it2, utype);
3643
3644         //reset afterwards
3645         if (tabular.is_long_tabular)
3646                 cnts.current_float(saveflt);
3647 }
3648
3649
3650 void InsetTabular::addToToc(DocIterator const & cpit)
3651 {
3652         DocIterator dit = cpit;
3653         dit.forwardPos();
3654         size_t const end = dit.nargs();
3655         for ( ; dit.idx() < end; dit.top().forwardIdx())
3656                 cell(dit.idx())->addToToc(dit);
3657 }
3658
3659
3660 void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
3661 {
3662         LYXERR(Debug::DEBUG, "# InsetTabular::doDispatch: cmd: " << cmd
3663                              << "\n  cur:" << cur);
3664         CursorSlice sl = cur.top();
3665         Cursor & bvcur = cur.bv().cursor();
3666
3667         FuncCode const act = cmd.action();
3668         
3669         switch (act) {
3670
3671         case LFUN_MOUSE_PRESS: {
3672                 //lyxerr << "# InsetTabular::MousePress\n" << cur.bv().cursor() << endl;
3673                 // select row
3674                 if (cmd.x() < xo(cur.bv()) + ADD_TO_TABULAR_WIDTH
3675                         || cmd.x() > xo(cur.bv()) + tabular.width()) {
3676                         row_type r = rowFromY(cur, cmd.y());
3677                         cur.idx() = tabular.getFirstCellInRow(r);
3678                         cur.pos() = 0;
3679                         cur.resetAnchor();
3680                         cur.idx() = tabular.getLastCellInRow(r);
3681                         cur.pos() = cur.lastpos();
3682                         cur.setSelection(true);
3683                         bvcur = cur; 
3684                         rowselect_ = true;
3685                         break;
3686                 }
3687                 // select column
3688                 int const y0 = yo(cur.bv()) - tabular.rowAscent(0);
3689                 if (cmd.y() < y0 + ADD_TO_TABULAR_WIDTH 
3690                         || cmd.y() > y0 + tabular.height()) {
3691                         col_type c = columnFromX(cur, cmd.x());
3692                         cur.idx() = tabular.cellIndex(0, c);
3693                         cur.pos() = 0;
3694                         cur.resetAnchor();
3695                         cur.idx() = tabular.cellIndex(tabular.nrows() - 1, c);
3696                         cur.pos() = cur.lastpos();
3697                         cur.setSelection(true);
3698                         bvcur = cur; 
3699                         colselect_ = true;
3700                         break;
3701                 }
3702                 // do not reset cursor/selection if we have selected
3703                 // some cells (bug 2715).
3704                 if (cmd.button() == mouse_button::button3
3705                     && &bvcur.selBegin().inset() == this 
3706                     && bvcur.selIsMultiCell()) 
3707                         ;
3708                 else
3709                         // Let InsetTableCell do it
3710                         cell(cur.idx())->dispatch(cur, cmd);
3711                 break;
3712         }
3713         case LFUN_MOUSE_MOTION:
3714                 //lyxerr << "# InsetTabular::MouseMotion\n" << bvcur << endl;
3715                 if (cmd.button() == mouse_button::button1) {
3716                         // only accept motions to places not deeper nested than the real anchor
3717                         if (!bvcur.realAnchor().hasPart(cur)) {
3718                                 cur.undispatched();
3719                                 break;
3720                         }
3721                         // select (additional) row
3722                         if (rowselect_) {
3723                                 row_type r = rowFromY(cur, cmd.y());
3724                                 cur.idx() = tabular.getLastCellInRow(r);
3725                                 // we need to reset the cursor's pit and pos now, as the old ones
3726                                 // may no longer be valid.
3727                                 cur.pit() = 0;
3728                                 cur.pos() = 0;
3729                                 bvcur.setCursor(cur);
3730                                 bvcur.setSelection(true);
3731                                 break;
3732                         }
3733                         // select (additional) column
3734                         if (colselect_) {
3735                                 col_type c = columnFromX(cur, cmd.x());
3736                                 cur.idx() = tabular.cellIndex(tabular.nrows() - 1, c);
3737                                 // we need to reset the cursor's pit and pos now, as the old ones
3738                                 // may no longer be valid.
3739                                 cur.pit() = 0;
3740                                 cur.pos() = 0;
3741                                 bvcur.setCursor(cur);
3742                                 bvcur.setSelection(true);
3743                                 break;
3744                         }
3745                         // only update if selection changes
3746                         if (bvcur.idx() == cur.idx() &&
3747                                 !(bvcur.realAnchor().idx() == cur.idx() && bvcur.pos() != cur.pos()))
3748                                 cur.noScreenUpdate();
3749                         setCursorFromCoordinates(cur, cmd.x(), cmd.y());
3750                         bvcur.setCursor(cur);
3751                         bvcur.setSelection(true);
3752                         // if this is a multicell selection, we just set the cursor to
3753                         // the beginning of the cell's text.
3754                         if (bvcur.selIsMultiCell()) {
3755                                 bvcur.pit() = bvcur.lastpit();
3756                                 bvcur.pos() = bvcur.lastpos();
3757                         }
3758                 }
3759                 break;
3760
3761         case LFUN_MOUSE_RELEASE:
3762                 rowselect_ = false;
3763                 colselect_ = false;
3764                 break;
3765
3766         case LFUN_CELL_BACKWARD:
3767                 movePrevCell(cur);
3768                 cur.setSelection(false);
3769                 break;
3770
3771         case LFUN_CELL_FORWARD:
3772                 moveNextCell(cur);
3773                 cur.setSelection(false);
3774                 break;
3775
3776         case LFUN_CHAR_FORWARD_SELECT:
3777         case LFUN_CHAR_FORWARD:
3778         case LFUN_CHAR_BACKWARD_SELECT:
3779         case LFUN_CHAR_BACKWARD:
3780         case LFUN_CHAR_RIGHT_SELECT:
3781         case LFUN_CHAR_RIGHT:
3782         case LFUN_CHAR_LEFT_SELECT:
3783         case LFUN_CHAR_LEFT: 
3784         case LFUN_WORD_FORWARD:
3785         case LFUN_WORD_FORWARD_SELECT:
3786         case LFUN_WORD_BACKWARD:
3787         case LFUN_WORD_BACKWARD_SELECT:
3788         case LFUN_WORD_RIGHT:
3789         case LFUN_WORD_RIGHT_SELECT:
3790         case LFUN_WORD_LEFT:
3791         case LFUN_WORD_LEFT_SELECT: {
3792                 // determine whether we move to next or previous cell, where to enter 
3793                 // the new cell from, and which command to "finish" (i.e., exit the
3794                 // inset) with:
3795                 bool next_cell;
3796                 EntryDirection entry_from = ENTRY_DIRECTION_IGNORE;
3797                 FuncCode finish_lfun;
3798
3799                 if (act == LFUN_CHAR_FORWARD 
3800                                 || act == LFUN_CHAR_FORWARD_SELECT
3801                                 || act == LFUN_WORD_FORWARD
3802                                 || act == LFUN_WORD_FORWARD_SELECT) {
3803                         next_cell = true;
3804                         finish_lfun = LFUN_FINISHED_FORWARD;
3805                 }
3806                 else if (act == LFUN_CHAR_BACKWARD
3807                                 || act == LFUN_CHAR_BACKWARD_SELECT
3808                                 || act == LFUN_WORD_BACKWARD
3809                                 || act == LFUN_WORD_BACKWARD_SELECT) {
3810                         next_cell = false;
3811                         finish_lfun = LFUN_FINISHED_BACKWARD;
3812                 }
3813                 // LEFT or RIGHT commands --- the interpretation will depend on the 
3814                 // table's direction.
3815                 else {
3816                         bool const right = act == LFUN_CHAR_RIGHT
3817                                 || act == LFUN_CHAR_RIGHT_SELECT
3818                                 || act == LFUN_WORD_RIGHT
3819                                 || act == LFUN_WORD_RIGHT_SELECT;
3820                         next_cell = isRightToLeft(cur) != right;
3821                         
3822                         if (lyxrc.visual_cursor)
3823                                 entry_from = right ? ENTRY_DIRECTION_LEFT:ENTRY_DIRECTION_RIGHT;
3824
3825                         finish_lfun = right ? LFUN_FINISHED_RIGHT : LFUN_FINISHED_LEFT;
3826                 }
3827
3828                 bool const select =     act == LFUN_CHAR_FORWARD_SELECT 
3829                     || act == LFUN_CHAR_BACKWARD_SELECT
3830                     || act == LFUN_CHAR_RIGHT_SELECT
3831                     || act == LFUN_CHAR_LEFT_SELECT
3832                         || act == LFUN_WORD_FORWARD_SELECT
3833                         || act == LFUN_WORD_RIGHT_SELECT
3834                         || act == LFUN_WORD_BACKWARD_SELECT
3835                         || act == LFUN_WORD_LEFT_SELECT;
3836
3837                 // If we have a multicell selection or we're 
3838                 // not doing some LFUN_*_SELECT thing anyway...
3839                 if (!cur.selIsMultiCell() || !select) {
3840                         col_type const c = tabular.cellColumn(cur.idx());
3841                         row_type const r = tabular.cellRow(cur.idx());
3842                         // Are we trying to select the whole cell and is the whole cell 
3843                         // not yet selected?
3844                         bool const select_whole = select && !isCellSelected(cur, r, c) &&
3845                                 ((next_cell && cur.pit() == cur.lastpit() 
3846                                 && cur.pos() == cur.lastpos())
3847                                 || (!next_cell && cur.pit() == 0 && cur.pos() == 0));
3848
3849                         bool const empty_cell = cur.lastpos() == 0 && cur.lastpit() == 0;
3850
3851                         // ...try to dispatch to the cell's inset.
3852                         cell(cur.idx())->dispatch(cur, cmd);
3853
3854                         // When we already have a selection we want to select the whole cell
3855                         // before going to the next cell.
3856                         if (select_whole && !empty_cell){
3857                                 getText(cur.idx())->selectAll(cur);
3858                                 cur.dispatched();
3859                                 break;
3860                         }
3861
3862                         // FIXME: When we support the selection of an empty cell, remove 
3863                         // the !empty_cell from this condition. For now we jump to the next
3864                         // cell if the current cell is empty.
3865                         if (cur.result().dispatched() && !empty_cell)
3866                                 break;
3867                 }
3868
3869                 // move to next/prev cell, as appropriate
3870                 // note that we will always do this if we're selecting and we have
3871                 // a multicell selection
3872                 LYXERR(Debug::RTL, "entering " << (next_cell ? "next" : "previous")
3873                         << " cell from: " << int(entry_from));
3874                 if (next_cell)
3875                         moveNextCell(cur, entry_from);
3876                 else
3877                         movePrevCell(cur, entry_from);
3878                 // if we're exiting the table, call the appropriate FINISHED lfun
3879                 if (sl == cur.top())
3880                         cmd = FuncRequest(finish_lfun);
3881                 else
3882                         cur.dispatched();
3883                 break;
3884
3885         }
3886
3887         case LFUN_DOWN_SELECT:
3888         case LFUN_DOWN:
3889                 if (!(cur.selection() && cur.selIsMultiCell()))
3890                         cell(cur.idx())->dispatch(cur, cmd);
3891                 
3892                 cur.dispatched(); // override the cell's decision
3893                 if (sl == cur.top()) {
3894                         // if our Text didn't do anything to the cursor
3895                         // then we try to put the cursor into the cell below
3896                         // setting also the right targetX.
3897                         cur.selHandle(act == LFUN_DOWN_SELECT);
3898                         if (tabular.cellRow(cur.idx()) != tabular.nrows() - 1) {
3899                                 cur.idx() = tabular.cellBelow(cur.idx());
3900                                 cur.pit() = 0;
3901                                 TextMetrics const & tm =
3902                                         cur.bv().textMetrics(cell(cur.idx())->getText(0));
3903                                 cur.pos() = tm.x2pos(cur.pit(), 0, cur.targetX());
3904                                 cur.setCurrentFont();
3905                         }
3906                 }
3907                 if (sl == cur.top()) {
3908                         // we trick it to go to forward after leaving the
3909                         // tabular.
3910                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
3911                         cur.undispatched();
3912                 }
3913                 if (cur.selIsMultiCell()) {
3914                         cur.pit() = cur.lastpit();
3915                         cur.pos() = cur.lastpos();
3916                         cur.setCurrentFont();
3917                         return;
3918                 }
3919                 break;
3920
3921         case LFUN_UP_SELECT:
3922         case LFUN_UP:
3923                 if (!(cur.selection() && cur.selIsMultiCell()))
3924                         cell(cur.idx())->dispatch(cur, cmd);
3925                 cur.dispatched(); // override the cell's decision
3926                 if (sl == cur.top()) {
3927                         // if our Text didn't do anything to the cursor
3928                         // then we try to put the cursor into the cell above
3929                         // setting also the right targetX.
3930                         cur.selHandle(act == LFUN_UP_SELECT);
3931                         if (tabular.cellRow(cur.idx()) != 0) {
3932                                 cur.idx() = tabular.cellAbove(cur.idx());
3933                                 cur.pit() = cur.lastpit();
3934                                 Text const * text = cell(cur.idx())->getText(0);
3935                                 TextMetrics const & tm = cur.bv().textMetrics(text);
3936                                 ParagraphMetrics const & pm =
3937                                         tm.parMetrics(cur.lastpit());
3938                                 cur.pos() = tm.x2pos(cur.pit(), pm.rows().size()-1, cur.targetX());
3939                                 cur.setCurrentFont();
3940                         }
3941                 }
3942                 if (sl == cur.top()) {
3943                         cmd = FuncRequest(LFUN_UP);
3944                         cur.undispatched();
3945                 }
3946                 if (cur.selIsMultiCell()) {
3947                         cur.pit() = 0;
3948                         cur.pos() = cur.lastpos();
3949                         cur.setCurrentFont();
3950                         return;
3951                 }
3952                 break;
3953
3954 //      case LFUN_SCREEN_DOWN: {
3955 //              //if (hasSelection())
3956 //              //      cur.selection() = false;
3957 //              col_type const col = tabular.cellColumn(cur.idx());
3958 //              int const t =   cur.bv().top_y() + cur.bv().height();
3959 //              if (t < yo() + tabular.getHeightOfTabular()) {
3960 //                      cur.bv().scrollDocView(t, true);
3961 //                      cur.idx() = tabular.cellBelow(first_visible_cell) + col;
3962 //              } else {
3963 //                      cur.idx() = tabular.getFirstCellInRow(tabular.rows() - 1) + col;
3964 //              }
3965 //              cur.par() = 0;
3966 //              cur.pos() = 0;
3967 //              break;
3968 //      }
3969 //
3970 //      case LFUN_SCREEN_UP: {
3971 //              //if (hasSelection())
3972 //              //      cur.selection() = false;
3973 //              col_type const col = tabular.cellColumn(cur.idx());
3974 //              int const t =   cur.bv().top_y() + cur.bv().height();
3975 //              if (yo() < 0) {
3976 //                      cur.bv().scrollDocView(t, true);
3977 //                      if (yo() > 0)
3978 //                              cur.idx() = col;
3979 //                      else
3980 //                              cur.idx() = tabular.cellBelow(first_visible_cell) + col;
3981 //              } else {
3982 //                      cur.idx() = col;
3983 //              }
3984 //              cur.par() = cur.lastpar();
3985 //              cur.pos() = cur.lastpos();
3986 //              break;
3987 //      }
3988
3989         case LFUN_LAYOUT_TABULAR:
3990                 cur.bv().showDialog("tabular");
3991                 break;
3992
3993         case LFUN_INSET_MODIFY:
3994                 if (!tabularFeatures(cur, to_utf8(cmd.argument())))
3995                         cur.undispatched();
3996                 break;
3997
3998         // insert file functions
3999         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
4000         case LFUN_FILE_INSERT_PLAINTEXT:
4001                 // FIXME UNICODE
4002                 if (FileName::isAbsolute(to_utf8(cmd.argument()))) {
4003                         docstring const tmpstr = cur.bv().contentsOfPlaintextFile(
4004                                 FileName(to_utf8(cmd.argument())));
4005                         if (tmpstr.empty())
4006                                 break;
4007                         cur.recordUndoInset(INSERT_UNDO);
4008                         if (insertPlaintextString(cur.bv(), tmpstr, false)) {
4009                                 // content has been replaced,
4010                                 // so cursor might be invalid
4011                                 cur.pos() = cur.lastpos();
4012                                 cur.pit() = cur.lastpit();
4013                                 bvcur.setCursor(cur);
4014                         } else
4015                                 cur.undispatched();
4016                 }
4017                 break;
4018
4019         case LFUN_CUT:
4020                 if (cur.selIsMultiCell()) {
4021                         if (copySelection(cur)) {
4022                                 cur.recordUndoInset(DELETE_UNDO);
4023                                 cutSelection(cur);
4024                         }
4025                 } else
4026                         cell(cur.idx())->dispatch(cur, cmd);
4027                 break;
4028
4029         case LFUN_SELF_INSERT:
4030                 if (cur.selIsMultiCell()) {
4031                         cur.recordUndoInset(DELETE_UNDO);
4032                         cutSelection(cur);
4033                 }
4034                 cell(cur.idx())->dispatch(cur, cmd);
4035                 break;
4036
4037         case LFUN_CHAR_DELETE_BACKWARD:
4038         case LFUN_CHAR_DELETE_FORWARD:
4039                 if (cur.selIsMultiCell()) {
4040                         cur.recordUndoInset(DELETE_UNDO);
4041                         cutSelection(cur);
4042                 } else
4043                         cell(cur.idx())->dispatch(cur, cmd);
4044                 break;
4045
4046         case LFUN_COPY:
4047                 if (!cur.selection())
4048                         break;
4049                 if (cur.selIsMultiCell()) {
4050                         cur.finishUndo();
4051                         copySelection(cur);
4052                 } else
4053                         cell(cur.idx())->dispatch(cur, cmd);
4054                 break;
4055
4056         case LFUN_CLIPBOARD_PASTE:
4057         case LFUN_PRIMARY_SELECTION_PASTE: {
4058                 docstring const clip = (act == LFUN_CLIPBOARD_PASTE) ?
4059                         theClipboard().getAsText() :
4060                         theSelection().get();
4061                 if (clip.empty())
4062                         break;
4063                 // pass to InsertPlaintextString, but
4064                 // only if we have multi-cell content
4065                 if (clip.find_first_of(from_ascii("\t\n")) != docstring::npos) {
4066                         cur.recordUndoInset(INSERT_UNDO);
4067                         if (insertPlaintextString(cur.bv(), clip, false)) {
4068                                 // content has been replaced,
4069                                 // so cursor might be invalid
4070                                 cur.pos() = cur.lastpos();
4071                                 cur.pit() = cur.lastpit();
4072                                 bvcur.setCursor(cur);
4073                                 break;
4074                         }
4075                 }
4076                 // Let the cell handle normal text
4077                 cell(cur.idx())->dispatch(cur, cmd);
4078                 break;
4079         }
4080
4081         case LFUN_PASTE:
4082                 if (!tabularStackDirty()) {
4083                         if (!cur.selIsMultiCell())
4084                                 cell(cur.idx())->dispatch(cur, cmd);
4085                         break;
4086                 }
4087                 if (theClipboard().isInternal() ||
4088                     (!theClipboard().hasInternal() && theClipboard().hasLyXContents())) {
4089                         cur.recordUndoInset(INSERT_UNDO);
4090                         pasteClipboard(cur);
4091                 }
4092                 break;
4093
4094         case LFUN_FONT_EMPH:
4095         case LFUN_FONT_BOLD:
4096         case LFUN_FONT_BOLDSYMBOL:
4097         case LFUN_FONT_ROMAN:
4098         case LFUN_FONT_NOUN:
4099         case LFUN_FONT_ITAL:
4100         case LFUN_FONT_FRAK:
4101         case LFUN_FONT_TYPEWRITER:
4102         case LFUN_FONT_SANS:
4103         case LFUN_TEXTSTYLE_APPLY:
4104         case LFUN_TEXTSTYLE_UPDATE:
4105         case LFUN_FONT_SIZE:
4106         case LFUN_FONT_UNDERLINE:
4107         case LFUN_FONT_STRIKEOUT:
4108         case LFUN_FONT_UULINE:
4109         case LFUN_FONT_UWAVE:
4110         case LFUN_LANGUAGE:
4111         case LFUN_WORD_CAPITALIZE:
4112         case LFUN_WORD_UPCASE:
4113         case LFUN_WORD_LOWCASE:
4114         case LFUN_CHARS_TRANSPOSE:
4115                 if (cur.selIsMultiCell()) {
4116                         row_type rs, re;
4117                         col_type cs, ce;
4118                         getSelection(cur, rs, re, cs, ce);
4119                         Cursor tmpcur = cur;
4120                         for (row_type r = rs; r <= re; ++r) {
4121                                 for (col_type c = cs; c <= ce; ++c) {
4122                                         // cursor follows cell:
4123                                         tmpcur.idx() = tabular.cellIndex(r, c);
4124                                         // select this cell only:
4125                                         tmpcur.pit() = 0;
4126                                         tmpcur.pos() = 0;
4127                                         tmpcur.resetAnchor();
4128                                         tmpcur.pit() = tmpcur.lastpit();
4129                                         tmpcur.pos() = tmpcur.top().lastpos();
4130                                         tmpcur.setCursor(tmpcur);
4131                                         tmpcur.setSelection();
4132                                         cell(tmpcur.idx())->dispatch(tmpcur, cmd);
4133                                 }
4134                         }
4135                         break;
4136                 } else {
4137                         cell(cur.idx())->dispatch(cur, cmd);
4138                         break;
4139                 }
4140
4141         case LFUN_INSET_SETTINGS:
4142                 // relay this lfun to Inset, not to the cell.
4143                 Inset::doDispatch(cur, cmd);
4144                 break;
4145
4146         default:
4147                 // we try to handle this event in the insets dispatch function.
4148                 cell(cur.idx())->dispatch(cur, cmd);
4149                 break;
4150         }
4151 }
4152
4153
4154 // function sets an object as defined in func_status.h:
4155 // states OK, Unknown, Disabled, On, Off.
4156 bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
4157         FuncStatus & status) const
4158 {
4159         switch (cmd.action()) {
4160         case LFUN_INSET_MODIFY: {
4161                 if (&cur.inset() != this || cmd.getArg(0) != "tabular") 
4162                         break;
4163
4164                 string const s = cmd.getArg(1);
4165                 // FIXME: We only check for the very first argument...
4166                 int action = Tabular::LAST_ACTION;
4167                 int i = 0;
4168                 for (; tabularFeature[i].action != Tabular::LAST_ACTION; ++i) {
4169                         if (tabularFeature[i].feature == s) {
4170                                 action = tabularFeature[i].action;
4171                                 break;
4172                         }
4173                 }
4174                 if (action == Tabular::LAST_ACTION) {
4175                         status.clear();
4176                         status.setUnknown(true);
4177                         return true;
4178                 }
4179
4180                 string const argument = cmd.getLongArg(2);
4181
4182                 row_type sel_row_start = 0;
4183                 row_type sel_row_end = 0;
4184                 col_type sel_col_start = 0;
4185                 col_type sel_col_end = 0;
4186                 Tabular::ltType dummyltt;
4187                 bool flag = true;
4188
4189                 getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
4190
4191                 switch (action) {
4192                 case Tabular::SET_PWIDTH:
4193                 case Tabular::SET_MPWIDTH:
4194                 case Tabular::SET_SPECIAL_COLUMN:
4195                 case Tabular::SET_SPECIAL_MULTICOLUMN:
4196                 case Tabular::APPEND_ROW:
4197                 case Tabular::APPEND_COLUMN:
4198                 case Tabular::DELETE_ROW:
4199                 case Tabular::DELETE_COLUMN:
4200                 case Tabular::COPY_ROW:
4201                 case Tabular::COPY_COLUMN:
4202                 case Tabular::SET_TOP_SPACE:
4203                 case Tabular::SET_BOTTOM_SPACE:
4204                 case Tabular::SET_INTERLINE_SPACE:
4205                         status.clear();
4206                         return true;
4207
4208                 case Tabular::SET_DECIMAL_POINT:
4209                         status.setEnabled(
4210                                 tabular.getAlignment(cur.idx()) == LYX_ALIGN_DECIMAL);
4211                         break;
4212
4213                 case Tabular::SET_MULTICOLUMN:
4214                 case Tabular::UNSET_MULTICOLUMN:
4215                 case Tabular::MULTICOLUMN:
4216                         // If a row is set as longtable caption, it must not be allowed
4217                         // to unset that this row is a multicolumn.
4218                         status.setEnabled(sel_row_start == sel_row_end
4219                                 && !tabular.ltCaption(tabular.cellRow(cur.idx())));
4220                         status.setOnOff(tabular.isMultiColumn(cur.idx()));
4221                         break;
4222
4223                 case Tabular::SET_MULTIROW:
4224                 case Tabular::UNSET_MULTIROW:
4225                 case Tabular::MULTIROW:
4226                         // If a row is set as longtable caption, it must not be allowed
4227                         // to unset that this row is a multirow.
4228                         status.setEnabled(sel_col_start == sel_col_end
4229                                 && !tabular.ltCaption(tabular.cellRow(cur.idx())));
4230                         status.setOnOff(tabular.isMultiRow(cur.idx()));
4231                         break;
4232
4233                 case Tabular::SET_ALL_LINES:
4234                 case Tabular::UNSET_ALL_LINES:
4235                 case Tabular::SET_BORDER_LINES:
4236                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
4237                         break;
4238
4239                 case Tabular::SET_LINE_TOP:
4240                 case Tabular::SET_LINE_BOTTOM:
4241                 case Tabular::SET_LINE_LEFT:
4242                 case Tabular::SET_LINE_RIGHT:
4243                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
4244                         break;
4245
4246                 case Tabular::TOGGLE_LINE_TOP:
4247                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
4248                         status.setOnOff(tabular.topLine(cur.idx()));
4249                         break;
4250
4251                 case Tabular::TOGGLE_LINE_BOTTOM:
4252                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
4253                         status.setOnOff(tabular.bottomLine(cur.idx()));
4254                         break;
4255
4256                 case Tabular::TOGGLE_LINE_LEFT:
4257                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
4258                         status.setOnOff(tabular.leftLine(cur.idx()));
4259                         break;
4260
4261                 case Tabular::TOGGLE_LINE_RIGHT:
4262                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
4263                         status.setOnOff(tabular.rightLine(cur.idx()));
4264                         break;
4265
4266                 case Tabular::M_ALIGN_LEFT:
4267                         flag = false;
4268                 case Tabular::ALIGN_LEFT:
4269                         status.setEnabled(!tabular.isMultiRow(cur.idx()));
4270                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_LEFT);
4271                         break;
4272
4273                 case Tabular::M_ALIGN_RIGHT:
4274                         flag = false;
4275                 case Tabular::ALIGN_RIGHT:
4276                         status.setEnabled(!tabular.isMultiRow(cur.idx()));
4277                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_RIGHT);
4278                         break;
4279
4280                 case Tabular::M_ALIGN_CENTER:
4281                         flag = false;
4282                 case Tabular::ALIGN_CENTER:
4283                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_CENTER);
4284                         break;
4285
4286                 case Tabular::ALIGN_BLOCK:
4287                         status.setEnabled(!tabular.getPWidth(cur.idx()).zero()
4288                                 && !tabular.isMultiRow(cur.idx()));
4289                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_BLOCK);
4290                         break;
4291
4292                 case Tabular::ALIGN_DECIMAL:
4293                         status.setEnabled(!tabular.isMultiRow(cur.idx()) 
4294                                 && !tabular.isMultiColumn(cur.idx()));
4295                         status.setOnOff(tabular.getAlignment(cur.idx(), true) == LYX_ALIGN_DECIMAL);
4296                         break;
4297
4298                 case Tabular::M_VALIGN_TOP:
4299                         flag = false;
4300                 case Tabular::VALIGN_TOP:
4301                         status.setEnabled(!tabular.getPWidth(cur.idx()).zero()
4302                                 && !tabular.isMultiRow(cur.idx()));
4303                         status.setOnOff(
4304                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_TOP);
4305                         break;
4306
4307                 case Tabular::M_VALIGN_BOTTOM:
4308                         flag = false;
4309                 case Tabular::VALIGN_BOTTOM:
4310                         status.setEnabled(!tabular.getPWidth(cur.idx()).zero()
4311                                 && !tabular.isMultiRow(cur.idx()));
4312                         status.setOnOff(
4313                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_BOTTOM);
4314                         break;
4315
4316                 case Tabular::M_VALIGN_MIDDLE:
4317                         flag = false;
4318                 case Tabular::VALIGN_MIDDLE:
4319                         status.setEnabled(!tabular.getPWidth(cur.idx()).zero()
4320                                 && !tabular.isMultiRow(cur.idx()));
4321                         status.setOnOff(
4322                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_MIDDLE);
4323                         break;
4324
4325                 case Tabular::SET_LONGTABULAR:
4326                         // setting as longtable is not allowed when table is inside a float
4327                         if (cur.innerInsetOfType(FLOAT_CODE) != 0
4328                                 || cur.innerInsetOfType(WRAP_CODE) != 0)
4329                                 status.setEnabled(false);
4330                         else
4331                                 status.setEnabled(true);
4332                         status.setOnOff(tabular.is_long_tabular);
4333                         break;
4334
4335                 case Tabular::UNSET_LONGTABULAR:
4336                         status.setOnOff(!tabular.is_long_tabular);
4337                         break;
4338
4339                 case Tabular::TOGGLE_ROTATE_TABULAR:
4340                 case Tabular::SET_ROTATE_TABULAR:
4341                         status.setOnOff(tabular.rotate);
4342                         break;
4343
4344                 case Tabular::TABULAR_VALIGN_TOP:
4345                         status.setOnOff(tabular.tabular_valignment 
4346                                 == Tabular::LYX_VALIGN_TOP);
4347                         break;
4348                 case Tabular::TABULAR_VALIGN_MIDDLE:
4349                         status.setOnOff(tabular.tabular_valignment 
4350                                 == Tabular::LYX_VALIGN_MIDDLE);
4351                         break;
4352                 case Tabular::TABULAR_VALIGN_BOTTOM:
4353                         status.setOnOff(tabular.tabular_valignment 
4354                                 == Tabular::LYX_VALIGN_BOTTOM);
4355                         break;
4356
4357                 case Tabular::LONGTABULAR_ALIGN_LEFT:
4358                         status.setOnOff(tabular.longtabular_alignment 
4359                                 == Tabular::LYX_LONGTABULAR_ALIGN_LEFT);
4360                         break;
4361                 case Tabular::LONGTABULAR_ALIGN_CENTER:
4362                         status.setOnOff(tabular.longtabular_alignment 
4363                                 == Tabular::LYX_LONGTABULAR_ALIGN_CENTER);
4364                         break;
4365                 case Tabular::LONGTABULAR_ALIGN_RIGHT:
4366                         status.setOnOff(tabular.longtabular_alignment 
4367                                 == Tabular::LYX_LONGTABULAR_ALIGN_RIGHT);
4368                         break;
4369
4370                 case Tabular::UNSET_ROTATE_TABULAR:
4371                         status.setOnOff(!tabular.rotate);
4372                         break;
4373
4374                 case Tabular::TOGGLE_ROTATE_CELL:
4375                 case Tabular::SET_ROTATE_CELL:
4376                         status.setOnOff(!oneCellHasRotationState(false,
4377                                 sel_row_start, sel_row_end, sel_col_start, sel_col_end));
4378                         break;
4379
4380                 case Tabular::UNSET_ROTATE_CELL:
4381                         status.setOnOff(!oneCellHasRotationState(true,
4382                                 sel_row_start, sel_row_end, sel_col_start, sel_col_end));
4383                         break;
4384
4385                 case Tabular::SET_USEBOX:
4386                         status.setOnOff(convert<int>(argument) == tabular.getUsebox(cur.idx()));
4387                         break;
4388
4389                 // every row can only be one thing:
4390                 // either a footer or header or caption
4391                 case Tabular::SET_LTFIRSTHEAD:
4392                         status.setEnabled(sel_row_start == sel_row_end
4393                                 && !tabular.ltCaption(sel_row_start));
4394                         status.setOnOff(tabular.getRowOfLTFirstHead(sel_row_start, dummyltt));
4395                         break;
4396
4397                 case Tabular::UNSET_LTFIRSTHEAD:
4398                         status.setOnOff(!tabular.getRowOfLTFirstHead(sel_row_start, dummyltt));
4399                         break;
4400
4401                 case Tabular::SET_LTHEAD:
4402                         status.setEnabled(sel_row_start == sel_row_end
4403                                 && !tabular.ltCaption(sel_row_start));
4404                         status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
4405                         break;
4406
4407                 case Tabular::UNSET_LTHEAD:
4408                         status.setOnOff(!tabular.getRowOfLTHead(sel_row_start, dummyltt));
4409                         break;
4410
4411                 case Tabular::SET_LTFOOT:
4412                         status.setEnabled(sel_row_start == sel_row_end
4413                                 && !tabular.ltCaption(sel_row_start));
4414                         status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
4415                         break;
4416
4417                 case Tabular::UNSET_LTFOOT:
4418                         status.setOnOff(!tabular.getRowOfLTFoot(sel_row_start, dummyltt));
4419                         break;
4420
4421                 case Tabular::SET_LTLASTFOOT:
4422                         status.setEnabled(sel_row_start == sel_row_end
4423                                 && !tabular.ltCaption(sel_row_start));
4424                         status.setOnOff(tabular.getRowOfLTLastFoot(sel_row_start, dummyltt));
4425                         break;
4426
4427                 case Tabular::UNSET_LTLASTFOOT:
4428                         status.setOnOff(!tabular.getRowOfLTLastFoot(sel_row_start, dummyltt));
4429                         break;
4430
4431                 case Tabular::SET_LTNEWPAGE:
4432                         status.setOnOff(tabular.getLTNewPage(sel_row_start));
4433                         break;
4434
4435                 // only one row can be the caption
4436                 // and a multirow cannot be set as caption
4437                 case Tabular::SET_LTCAPTION:
4438                 case Tabular::UNSET_LTCAPTION:
4439                 case Tabular::TOGGLE_LTCAPTION:
4440                         status.setEnabled(sel_row_start == sel_row_end
4441                                 && !tabular.getRowOfLTFirstHead(sel_row_start, dummyltt)
4442                                 && !tabular.getRowOfLTHead(sel_row_start, dummyltt)
4443                                 && !tabular.getRowOfLTFoot(sel_row_start, dummyltt)
4444                                 && !tabular.getRowOfLTLastFoot(sel_row_start, dummyltt)
4445                                 && (!tabular.haveLTCaption()
4446                                         || tabular.ltCaption(sel_row_start))
4447                                 && !tabular.isMultiRow(sel_row_start));
4448                         status.setOnOff(tabular.ltCaption(sel_row_start));
4449                         break;
4450
4451                 case Tabular::SET_BOOKTABS:
4452                         status.setOnOff(tabular.use_booktabs);
4453                         break;
4454
4455                 case Tabular::UNSET_BOOKTABS:
4456                         status.setOnOff(!tabular.use_booktabs);
4457                         break;
4458
4459                 default:
4460                         status.clear();
4461                         status.setEnabled(false);
4462                         break;
4463                 }
4464                 return true;
4465         }
4466
4467         // These are only enabled inside tabular
4468         case LFUN_CELL_BACKWARD:
4469         case LFUN_CELL_FORWARD:
4470                 status.setEnabled(true);
4471                 return true;
4472
4473         // disable these with multiple cells selected
4474         case LFUN_INSET_INSERT:
4475         case LFUN_TABULAR_INSERT:
4476         case LFUN_FLEX_INSERT:
4477         case LFUN_FLOAT_INSERT:
4478         case LFUN_FLOAT_WIDE_INSERT:
4479         case LFUN_FOOTNOTE_INSERT:
4480         case LFUN_MARGINALNOTE_INSERT:
4481         case LFUN_MATH_INSERT:
4482         case LFUN_MATH_MODE:
4483         case LFUN_MATH_MUTATE:
4484         case LFUN_MATH_DISPLAY:
4485         case LFUN_NOTE_INSERT:
4486         case LFUN_ARGUMENT_INSERT:
4487         case LFUN_BOX_INSERT:
4488         case LFUN_BRANCH_INSERT:
4489         case LFUN_PHANTOM_INSERT:
4490         case LFUN_WRAP_INSERT:
4491         case LFUN_PREVIEW_INSERT:
4492         case LFUN_ERT_INSERT: {
4493                 if (cur.selIsMultiCell()) {
4494                         status.setEnabled(false);
4495                         return true;
4496                 } else
4497                         return cell(cur.idx())->getStatus(cur, cmd, status);
4498         }
4499
4500         // disable in non-fixed-width cells
4501         case LFUN_NEWLINE_INSERT:
4502         case LFUN_BREAK_PARAGRAPH: {
4503                 if (tabular.getPWidth(cur.idx()).zero()) {
4504                         status.setEnabled(false);
4505                         return true;
4506                 } else
4507                         return cell(cur.idx())->getStatus(cur, cmd, status);
4508         }
4509
4510         case LFUN_NEWPAGE_INSERT:
4511                 status.setEnabled(false);
4512                 return true;
4513
4514         case LFUN_PASTE:
4515                 if (tabularStackDirty() && theClipboard().isInternal()) {
4516                         if (cur.selIsMultiCell()) {
4517                                 row_type rs, re;
4518                                 col_type cs, ce;
4519                                 getSelection(cur, rs, re, cs, ce);
4520                                 if (paste_tabular && paste_tabular->ncols() == ce - cs + 1
4521                                           && paste_tabular->nrows() == re - rs + 1)
4522                                         status.setEnabled(true);        
4523                                 else {
4524                                         status.setEnabled(false);
4525                                         status.message(_("Selection size should match clipboard content."));
4526                                 }
4527                         } else
4528                                 status.setEnabled(true);
4529                         return true;
4530                 }
4531                 return cell(cur.idx())->getStatus(cur, cmd, status);
4532
4533         case LFUN_INSET_SETTINGS:
4534                 // relay this lfun to Inset, not to the cell.
4535                 return Inset::getStatus(cur, cmd, status);
4536
4537         default:
4538                 // we try to handle this event in the insets dispatch function.
4539                 return cell(cur.idx())->getStatus(cur, cmd, status);
4540         }
4541         return false;
4542 }
4543
4544
4545 Inset::DisplayType InsetTabular::display() const
4546 {
4547                 if (tabular.is_long_tabular) {
4548                         switch (tabular.longtabular_alignment) {
4549                         case Tabular::LYX_LONGTABULAR_ALIGN_LEFT:
4550                                 return AlignLeft;
4551                         case Tabular::LYX_LONGTABULAR_ALIGN_CENTER:
4552                                 return AlignCenter;
4553                         case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT:
4554                                 return AlignRight;
4555                         default:
4556                                 return AlignCenter;
4557                         }
4558                 } else
4559                         return Inline;
4560 }
4561
4562
4563 int InsetTabular::latex(odocstream & os, OutputParams const & runparams) const
4564 {
4565         return tabular.latex(os, runparams);
4566 }
4567
4568
4569 int InsetTabular::plaintext(odocstream & os, OutputParams const & runparams) const
4570 {
4571         os << '\n'; // output table on a new line
4572         int const dp = runparams.linelen > 0 ? runparams.depth : 0;
4573         tabular.plaintext(os, runparams, dp, false, 0);
4574         return PLAINTEXT_NEWLINE;
4575 }
4576
4577
4578 int InsetTabular::docbook(odocstream & os, OutputParams const & runparams) const
4579 {
4580         int ret = 0;
4581         Inset * master = 0;
4582
4583         // FIXME: Why not pass a proper DocIterator here?
4584 #if 0
4585         // if the table is inside a float it doesn't need the informaltable
4586         // wrapper. Search for it.
4587         for (master = owner(); master; master = master->owner())
4588                 if (master->lyxCode() == FLOAT_CODE)
4589                         break;
4590 #endif
4591
4592         if (!master) {
4593                 os << "<informaltable>";
4594                 ++ret;
4595         }
4596         ret += tabular.docbook(os, runparams);
4597         if (!master) {
4598                 os << "</informaltable>";
4599                 ++ret;
4600         }
4601         return ret;
4602 }
4603
4604
4605 docstring InsetTabular::xhtml(XHTMLStream & xs, OutputParams const & rp) const
4606 {
4607         // FIXME XHTML
4608         // It'd be better to be able to get this from an InsetLayout, but at present
4609         // InsetLayouts do not seem really to work for things that aren't InsetTexts.
4610         xs << html::StartTag("table");
4611         docstring ret = tabular.xhtml(xs, rp);
4612         xs << html::EndTag("table");
4613         return ret;
4614 }
4615
4616
4617 void InsetTabular::validate(LaTeXFeatures & features) const
4618 {
4619         tabular.validate(features);
4620         // FIXME XHTML
4621         // It'd be better to be able to get this from an InsetLayout, but at present
4622         // InsetLayouts do not seem really to work for things that aren't InsetTexts.
4623         if (features.runparams().flavor == OutputParams::HTML)
4624                 features.addPreambleSnippet("<style type=\"text/css\">\n"
4625       "table { border: 1px solid black; display: inline-block; }\n"
4626       "td { border: 1px solid black; padding: 0.5ex; }\n"
4627       "</style>");
4628 }
4629
4630
4631 shared_ptr<InsetTableCell const> InsetTabular::cell(idx_type idx) const
4632 {
4633         return tabular.cellInset(idx);
4634 }
4635
4636
4637 shared_ptr<InsetTableCell> InsetTabular::cell(idx_type idx)
4638 {
4639         return tabular.cellInset(idx);
4640 }
4641
4642
4643 void InsetTabular::cursorPos(BufferView const & bv,
4644                 CursorSlice const & sl, bool boundary, int & x, int & y) const
4645 {
4646         cell(sl.idx())->cursorPos(bv, sl, boundary, x, y);
4647
4648         int const row = tabular.cellRow(sl.idx());
4649         int const col = tabular.cellColumn(sl.idx());
4650
4651         // y offset     correction
4652         for (int r = 0; r < row; ++r)
4653                 y += tabular.rowDescent(r) + tabular.rowAscent(r + 1) 
4654                         + tabular.interRowSpace(r + 1);
4655
4656         y += tabular.textVOffset(sl.idx());
4657
4658         // x offset correction
4659         for (int c = 0; c < col; ++c)
4660                 x += tabular.column_info[c].width;
4661         
4662         x += tabular.textHOffset(sl.idx());
4663         x += ADD_TO_TABULAR_WIDTH;
4664         x += scx_;
4665 }
4666
4667
4668 int InsetTabular::dist(BufferView & bv, idx_type const cell, int x, int y) const
4669 {
4670         int xx = 0;
4671         int yy = 0;
4672         Inset const & inset = *tabular.cellInset(cell);
4673         Point o = bv.coordCache().getInsets().xy(&inset);
4674         int const xbeg = o.x_ - tabular.textHOffset(cell);
4675         int const xend = xbeg + tabular.cellWidth(cell);
4676         row_type const row = tabular.cellRow(cell);
4677         int const ybeg = o.y_ - tabular.rowAscent(row)
4678                 - tabular.interRowSpace(row);
4679         int const yend = ybeg + tabular.cellHeight(cell);
4680
4681         if (x < xbeg)
4682                 xx = xbeg - x;
4683         else if (x > xend)
4684                 xx = x - xend;
4685
4686         if (y < ybeg)
4687                 yy = ybeg - y;
4688         else if (y > yend)
4689                 yy = y - yend;
4690
4691         //lyxerr << " xbeg=" << xbeg << "  xend=" << xend
4692         //       << " ybeg=" << ybeg << " yend=" << yend
4693         //       << " xx=" << xx << " yy=" << yy
4694         //       << " dist=" << xx + yy << endl;
4695         return xx + yy;
4696 }
4697
4698
4699 Inset * InsetTabular::editXY(Cursor & cur, int x, int y)
4700 {
4701         //lyxerr << "InsetTabular::editXY: " << this << endl;
4702         cur.setSelection(false);
4703         cur.push(*this);
4704         cur.idx() = getNearestCell(cur.bv(), x, y);
4705         resetPos(cur);
4706         return cur.bv().textMetrics(&cell(cur.idx())->text()).editXY(cur, x, y);
4707 }
4708
4709
4710 void InsetTabular::setCursorFromCoordinates(Cursor & cur, int x, int y) const
4711 {
4712         cur.idx() = getNearestCell(cur.bv(), x, y);
4713         cur.bv().textMetrics(&cell(cur.idx())->text()).setCursorFromCoordinates(cur, x, y);
4714 }
4715
4716
4717 InsetTabular::idx_type InsetTabular::getNearestCell(BufferView & bv, int x, int y) const
4718 {
4719         idx_type idx_min = 0;
4720         int dist_min = numeric_limits<int>::max();
4721         for (idx_type i = 0, n = nargs(); i != n; ++i) {
4722                 if (bv.coordCache().getInsets().has(tabular.cellInset(i).get())) {
4723                         int const d = dist(bv, i, x, y);
4724                         if (d < dist_min) {
4725                                 dist_min = d;
4726                                 idx_min = i;
4727                         }
4728                 }
4729         }
4730         return idx_min;
4731 }
4732
4733
4734 int InsetTabular::cellXPos(idx_type const cell) const
4735 {
4736         col_type col = tabular.cellColumn(cell);
4737         int lx = 0;
4738         for (col_type c = 0; c < col; ++c)
4739                 lx += tabular.column_info[c].width;
4740
4741         return lx;
4742 }
4743
4744
4745 void InsetTabular::resetPos(Cursor & cur) const
4746 {
4747         BufferView & bv = cur.bv();
4748         int const maxwidth = bv.workWidth();
4749
4750         int const scx_old = scx_;
4751         int const i = cur.find(this);
4752         if (i == -1) {
4753                 scx_ = 0;
4754         } else {
4755                 int const X1 = 0;
4756                 int const X2 = maxwidth;
4757                 int const offset = ADD_TO_TABULAR_WIDTH + 2;
4758                 int const x1 = xo(cur.bv()) + cellXPos(cur[i].idx()) + offset;
4759                 int const x2 = x1 + tabular.cellWidth(cur[i].idx());
4760
4761                 if (x1 < X1)
4762                         scx_ = X1 + 20 - x1;
4763                 else if (x2 > X2)
4764                         scx_ = X2 - 20 - x2;
4765                 else
4766                         scx_ = 0;
4767         }
4768
4769         // only update if offset changed
4770         if (scx_ != scx_old)
4771                 cur.screenUpdateFlags(Update::Force | Update::FitCursor);
4772 }
4773
4774
4775 void InsetTabular::moveNextCell(Cursor & cur, EntryDirection entry_from)
4776 {
4777         row_type const row = tabular.cellRow(cur.idx());
4778         col_type const col = tabular.cellColumn(cur.idx());
4779
4780         if (isRightToLeft(cur)) {
4781                 if (tabular.cellColumn(cur.idx()) == 0) {
4782                         if (row == tabular.nrows() - 1)
4783                                 return;
4784                         cur.idx() = tabular.cellBelow(tabular.getLastCellInRow(row));
4785                 } else {
4786                         if (cur.idx() == 0)
4787                                 return;
4788                         if (col == 0)
4789                                 cur.idx() = tabular.getLastCellInRow(row - 1);
4790                         else
4791                                 cur.idx() = tabular.cellIndex(row, col - 1);
4792                 }
4793         } else {
4794                 if (tabular.isLastCell(cur.idx()))
4795                         return;
4796                 if (cur.idx() == tabular.getLastCellInRow(row))
4797                         cur.idx() = tabular.cellIndex(row + 1, 0);
4798                 else {
4799                         col_type const colnextcell = col + tabular.columnSpan(cur.idx());
4800                         cur.idx() = tabular.cellIndex(row, colnextcell);
4801                 }
4802         }
4803
4804         cur.boundary(false);
4805
4806         if (cur.selIsMultiCell()) {
4807                 cur.pit() = cur.lastpit();
4808                 cur.pos() = cur.lastpos();
4809                 resetPos(cur);
4810                 return;
4811         }
4812
4813         cur.pit() = 0;
4814         cur.pos() = 0;
4815
4816         // in visual mode, place cursor at extreme left or right
4817         
4818         switch(entry_from) {
4819
4820         case ENTRY_DIRECTION_RIGHT:
4821                 cur.posVisToRowExtremity(false /* !left */);
4822                 break;
4823         case ENTRY_DIRECTION_LEFT:
4824                 cur.posVisToRowExtremity(true /* left */);
4825                 break;
4826         case ENTRY_DIRECTION_IGNORE:
4827                 // nothing to do in this case
4828                 break;
4829
4830         }
4831         cur.setCurrentFont();
4832         resetPos(cur);
4833 }
4834
4835
4836 void InsetTabular::movePrevCell(Cursor & cur, EntryDirection entry_from)
4837 {
4838         row_type const row = tabular.cellRow(cur.idx());
4839         col_type const col = tabular.cellColumn(cur.idx());
4840
4841         if (isRightToLeft(cur)) {
4842                 if (cur.idx() == tabular.getLastCellInRow(row)) {
4843                         if (row == 0)
4844                                 return;
4845                         cur.idx() = tabular.getFirstCellInRow(row);
4846                         cur.idx() = tabular.cellAbove(cur.idx());
4847                 } else {
4848                         if (tabular.isLastCell(cur.idx()))
4849                                 return;
4850                         if (cur.idx() == tabular.getLastCellInRow(row))
4851                                 cur.idx() = tabular.cellIndex(row + 1, 0);
4852                         else
4853                                 cur.idx() = tabular.cellIndex(row, col + 1);
4854                 }
4855         } else {
4856                 if (cur.idx() == 0) // first cell
4857                         return;
4858                 if (col == 0)
4859                         cur.idx() = tabular.getLastCellInRow(row - 1);
4860                 else
4861                         cur.idx() = tabular.cellIndex(row, col - 1);
4862         }
4863
4864         if (cur.selIsMultiCell()) {
4865                 cur.pit() = cur.lastpit();
4866                 cur.pos() = cur.lastpos();
4867                 resetPos(cur);
4868                 return;
4869         }
4870
4871         cur.pit() = cur.lastpit();
4872         cur.pos() = cur.lastpos();
4873
4874         // in visual mode, place cursor at extreme left or right
4875         
4876         switch(entry_from) {
4877
4878         case ENTRY_DIRECTION_RIGHT:
4879                 cur.posVisToRowExtremity(false /* !left */);
4880                 break;
4881         case ENTRY_DIRECTION_LEFT:
4882                 cur.posVisToRowExtremity(true /* left */);
4883                 break;
4884         case ENTRY_DIRECTION_IGNORE:
4885                 // nothing to do in this case
4886                 break;
4887
4888         }
4889         cur.setCurrentFont();
4890         resetPos(cur);
4891 }
4892
4893
4894 bool InsetTabular::tabularFeatures(Cursor & cur, string const & argument)
4895 {
4896         istringstream is(argument);
4897         string s;
4898         is >> s;
4899         if (insetCode(s) != TABULAR_CODE)
4900                 return false;
4901
4902         // Safe guard.
4903         size_t safe_guard = 0;
4904         for (;;) {
4905                 if (is.eof())
4906                         break;
4907                 safe_guard++;
4908                 if (safe_guard > 1000) {
4909                         LYXERR0("parameter max count reached!");
4910                         break;
4911                 }
4912                 is >> s;
4913                 Tabular::Feature action = Tabular::LAST_ACTION;
4914
4915                 size_t i = 0;
4916                 for (; tabularFeature[i].action != Tabular::LAST_ACTION; ++i) {
4917                         if (s != tabularFeature[i].feature)
4918                                 continue;
4919
4920                         action = tabularFeature[i].action;
4921                         break;
4922                 }
4923                 if (action == Tabular::LAST_ACTION) {
4924                         LYXERR0("Feature not found " << s);
4925                         continue;
4926                 }
4927                 string val;
4928                 if (tabularFeature[i].need_value)
4929                         is >> val;
4930                 LYXERR(Debug::DEBUG, "Feature: " << s << "\t\tvalue: " << val);
4931                 tabularFeatures(cur, action, val);
4932         }
4933         return true;
4934 }
4935
4936
4937 static void checkLongtableSpecial(Tabular::ltType & ltt,
4938                           string const & special, bool & flag)
4939 {
4940         if (special == "dl_above") {
4941                 ltt.topDL = flag;
4942                 ltt.set = false;
4943         } else if (special == "dl_below") {
4944                 ltt.bottomDL = flag;
4945                 ltt.set = false;
4946         } else if (special == "empty") {
4947                 ltt.empty = flag;
4948                 ltt.set = false;
4949         } else if (flag) {
4950                 ltt.empty = false;
4951                 ltt.set = true;
4952         }
4953 }
4954
4955
4956 bool InsetTabular::oneCellHasRotationState(bool rotated,
4957                 row_type row_start, row_type row_end,
4958                 col_type col_start, col_type col_end) const 
4959 {
4960         for (row_type r = row_start; r <= row_end; ++r)
4961                 for (col_type c = col_start; c <= col_end; ++c)
4962                         if (tabular.getRotateCell(tabular.cellIndex(r, c)) == rotated)
4963                                 return true;
4964
4965         return false;
4966 }
4967
4968 void InsetTabular::tabularFeatures(Cursor & cur,
4969         Tabular::Feature feature, string const & value)
4970 {
4971         col_type sel_col_start;
4972         col_type sel_col_end;
4973         row_type sel_row_start;
4974         row_type sel_row_end;
4975         bool setLines = false;
4976         LyXAlignment setAlign = LYX_ALIGN_LEFT;
4977         Tabular::VAlignment setVAlign = Tabular::LYX_VALIGN_TOP;
4978
4979         switch (feature) {
4980
4981         case Tabular::M_ALIGN_LEFT:
4982         case Tabular::ALIGN_LEFT:
4983                 setAlign = LYX_ALIGN_LEFT;
4984                 break;
4985
4986         case Tabular::M_ALIGN_RIGHT:
4987         case Tabular::ALIGN_RIGHT:
4988                 setAlign = LYX_ALIGN_RIGHT;
4989                 break;
4990
4991         case Tabular::M_ALIGN_CENTER:
4992         case Tabular::ALIGN_CENTER:
4993                 setAlign = LYX_ALIGN_CENTER;
4994                 break;
4995
4996         case Tabular::ALIGN_BLOCK:
4997                 setAlign = LYX_ALIGN_BLOCK;
4998                 break;
4999
5000         case Tabular::ALIGN_DECIMAL:
5001                 setAlign = LYX_ALIGN_DECIMAL;
5002                 break;
5003
5004         case Tabular::M_VALIGN_TOP:
5005         case Tabular::VALIGN_TOP:
5006                 setVAlign = Tabular::LYX_VALIGN_TOP;
5007                 break;
5008
5009         case Tabular::M_VALIGN_BOTTOM:
5010         case Tabular::VALIGN_BOTTOM:
5011                 setVAlign = Tabular::LYX_VALIGN_BOTTOM;
5012                 break;
5013
5014         case Tabular::M_VALIGN_MIDDLE:
5015         case Tabular::VALIGN_MIDDLE:
5016                 setVAlign = Tabular::LYX_VALIGN_MIDDLE;
5017                 break;
5018
5019         default:
5020                 break;
5021         }
5022
5023         cur.recordUndoInset(ATOMIC_UNDO);
5024
5025         getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
5026         row_type const row = tabular.cellRow(cur.idx());
5027         col_type const column = tabular.cellColumn(cur.idx());
5028         bool flag = true;
5029         Tabular::ltType ltt;
5030
5031         switch (feature) {
5032
5033         case Tabular::SET_PWIDTH: {
5034                 Length const len(value);
5035                 tabular.setColumnPWidth(cur, cur.idx(), len);
5036                 if (len.zero()
5037                     && tabular.getAlignment(cur.idx(), true) == LYX_ALIGN_BLOCK)
5038                         tabularFeatures(cur, Tabular::ALIGN_CENTER, string());
5039                 break;
5040         }
5041
5042         case Tabular::SET_MPWIDTH:
5043                 tabular.setMColumnPWidth(cur, cur.idx(), Length(value));
5044                 break;
5045
5046         case Tabular::SET_SPECIAL_COLUMN:
5047         case Tabular::SET_SPECIAL_MULTICOLUMN:
5048                 if (value == "none")
5049                         tabular.setAlignSpecial(cur.idx(), docstring(), feature);
5050                 else
5051                         tabular.setAlignSpecial(cur.idx(), from_utf8(value), feature);
5052                 break;
5053
5054         case Tabular::APPEND_ROW:
5055                 // append the row into the tabular
5056                 tabular.appendRow(cur.idx());
5057                 break;
5058
5059         case Tabular::APPEND_COLUMN:
5060                 // append the column into the tabular
5061                 tabular.appendColumn(cur.idx());
5062                 cur.idx() = tabular.cellIndex(row, column);
5063                 break;
5064
5065         case Tabular::DELETE_ROW:
5066                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5067                         tabular.deleteRow(sel_row_start);
5068                 if (sel_row_start >= tabular.nrows())
5069                         --sel_row_start;
5070                 cur.idx() = tabular.cellIndex(sel_row_start, column);
5071                 cur.pit() = 0;
5072                 cur.pos() = 0;
5073                 cur.setSelection(false);
5074                 break;
5075
5076         case Tabular::DELETE_COLUMN:
5077                 for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5078                         tabular.deleteColumn(sel_col_start);
5079                 if (sel_col_start >= tabular.ncols())
5080                         --sel_col_start;
5081                 cur.idx() = tabular.cellIndex(row, sel_col_start);
5082                 cur.pit() = 0;
5083                 cur.pos() = 0;
5084                 cur.setSelection(false);
5085                 break;
5086
5087         case Tabular::COPY_ROW:
5088                 tabular.copyRow(row);
5089                 break;
5090
5091         case Tabular::COPY_COLUMN:
5092                 tabular.copyColumn(column);
5093                 cur.idx() = tabular.cellIndex(row, column);
5094                 break;
5095
5096         case Tabular::SET_LINE_TOP:
5097         case Tabular::TOGGLE_LINE_TOP: {
5098                 bool lineSet = (feature == Tabular::SET_LINE_TOP)
5099                                ? (value == "true") : !tabular.topLine(cur.idx());
5100                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5101                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5102                                 tabular.setTopLine(tabular.cellIndex(r, c), lineSet);
5103                 break;
5104         }
5105
5106         case Tabular::SET_LINE_BOTTOM:
5107         case Tabular::TOGGLE_LINE_BOTTOM: {
5108                 bool lineSet = (feature == Tabular::SET_LINE_BOTTOM)
5109                                ? (value == "true") : !tabular.bottomLine(cur.idx());
5110                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5111                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5112                                 tabular.setBottomLine(tabular.cellIndex(r, c), lineSet);
5113                 break;
5114         }
5115
5116         case Tabular::SET_LINE_LEFT:
5117         case Tabular::TOGGLE_LINE_LEFT: {
5118                 bool lineSet = (feature == Tabular::SET_LINE_LEFT)
5119                                ? (value == "true") : !tabular.leftLine(cur.idx());
5120                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5121                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5122                                 tabular.setLeftLine(tabular.cellIndex(r, c), lineSet);
5123                 break;
5124         }
5125
5126         case Tabular::SET_LINE_RIGHT:
5127         case Tabular::TOGGLE_LINE_RIGHT: {
5128                 bool lineSet = (feature == Tabular::SET_LINE_RIGHT)
5129                                ? (value == "true") : !tabular.rightLine(cur.idx());
5130                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5131                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5132                                 tabular.setRightLine(tabular.cellIndex(r, c), lineSet);
5133                 break;
5134         }
5135
5136         case Tabular::M_ALIGN_LEFT:
5137         case Tabular::M_ALIGN_RIGHT:
5138         case Tabular::M_ALIGN_CENTER:
5139                 flag = false;
5140         case Tabular::ALIGN_LEFT:
5141         case Tabular::ALIGN_RIGHT:
5142         case Tabular::ALIGN_CENTER:
5143         case Tabular::ALIGN_BLOCK:
5144         case Tabular::ALIGN_DECIMAL:
5145                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5146                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5147                                 tabular.setAlignment(tabular.cellIndex(r, c), setAlign, flag);
5148                 break;
5149
5150         case Tabular::M_VALIGN_TOP:
5151         case Tabular::M_VALIGN_BOTTOM:
5152         case Tabular::M_VALIGN_MIDDLE:
5153                 flag = false;
5154         case Tabular::VALIGN_TOP:
5155         case Tabular::VALIGN_BOTTOM:
5156         case Tabular::VALIGN_MIDDLE:
5157                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5158                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5159                                 tabular.setVAlignment(tabular.cellIndex(r, c), setVAlign, flag);
5160                 break;
5161
5162         case Tabular::SET_MULTICOLUMN: {
5163                 if (!cur.selection()) {
5164                         // just multicol for one single cell
5165                         // check whether we are completely in a multicol
5166                         if (!tabular.isMultiColumn(cur.idx()))
5167                                 tabular.setMultiColumn(cur.idx(), 1);
5168                         break;
5169                 }
5170                 // we have a selection so this means we just add all this
5171                 // cells to form a multicolumn cell
5172                 idx_type const s_start = cur.selBegin().idx();
5173                 row_type const col_start = tabular.cellColumn(s_start);
5174                 row_type const col_end = tabular.cellColumn(cur.selEnd().idx());
5175                 cur.idx() = tabular.setMultiColumn(s_start, col_end - col_start + 1);
5176                 cur.pit() = 0;
5177                 cur.pos() = 0;
5178                 cur.setSelection(false);
5179                 break;
5180         }
5181
5182         case Tabular::UNSET_MULTICOLUMN: {
5183                 if (!cur.selection()) {
5184                         if (tabular.isMultiColumn(cur.idx()))
5185                                 tabular.unsetMultiColumn(cur.idx());
5186                         break;
5187                 }
5188                 break;
5189         }
5190
5191         case Tabular::MULTICOLUMN: {
5192                 if (tabular.isMultiColumn(cur.idx()))
5193                         tabularFeatures(cur, Tabular::UNSET_MULTICOLUMN);
5194                 else
5195                         tabularFeatures(cur, Tabular::SET_MULTICOLUMN);
5196                 break;
5197         }
5198
5199         case Tabular::SET_MULTIROW: {
5200                 if (!cur.selection()) {
5201                         // just multirow for one single cell
5202                         // check whether we are completely in a multirow
5203                         if (!tabular.isMultiRow(cur.idx()))
5204                                 tabular.setMultiRow(cur.idx(), 1);
5205                         break;
5206                 }
5207                 // we have a selection so this means we just add all this
5208                 // cells to form a multirow cell
5209                 idx_type const s_start = cur.selBegin().idx();
5210                 row_type const row_start = tabular.cellRow(s_start);
5211                 row_type const row_end = tabular.cellRow(cur.selEnd().idx());
5212                 cur.idx() = tabular.setMultiRow(s_start, row_end - row_start + 1);
5213                 cur.pit() = 0;
5214                 cur.pos() = 0;
5215                 cur.setSelection(false);
5216                 break;
5217         }
5218
5219         case Tabular::UNSET_MULTIROW: {
5220                 if (!cur.selection()) {
5221                         if (tabular.isMultiRow(cur.idx()))
5222                                 tabular.unsetMultiRow(cur.idx());
5223                         break;
5224                 }
5225                 break;
5226         }
5227
5228         case Tabular::MULTIROW: {
5229                 if (tabular.isMultiRow(cur.idx()))
5230                         tabularFeatures(cur, Tabular::UNSET_MULTIROW);
5231                 else
5232                         tabularFeatures(cur, Tabular::SET_MULTIROW);
5233                 break;
5234         }
5235
5236         case Tabular::SET_ALL_LINES:
5237                 setLines = true;
5238         case Tabular::UNSET_ALL_LINES:
5239                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5240                         for (col_type c = sel_col_start; c <= sel_col_end; ++c) {
5241                                 idx_type const cell = tabular.cellIndex(r, c);
5242                                 tabular.setTopLine(cell, setLines);
5243                                 tabular.setBottomLine(cell, setLines);
5244                                 tabular.setRightLine(cell, setLines);
5245                                 tabular.setLeftLine(cell, setLines);
5246                         }
5247                 break;
5248
5249         case Tabular::SET_BORDER_LINES:
5250                 for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
5251                         tabular.setLeftLine(tabular.cellIndex(r, sel_col_start), true);
5252                         tabular.setRightLine(tabular.cellIndex(r, sel_col_end), true);
5253                 }
5254                 for (col_type c = sel_col_start; c <= sel_col_end; ++c) {
5255                         tabular.setTopLine(tabular.cellIndex(sel_row_start, c), true);
5256                         tabular.setBottomLine(tabular.cellIndex(sel_row_end, c), true);
5257                 }
5258                 break;
5259
5260         case Tabular::SET_LONGTABULAR:
5261                 tabular.is_long_tabular = true;
5262                 break;
5263
5264         case Tabular::UNSET_LONGTABULAR:
5265                 for (row_type r = 0; r < tabular.nrows(); ++r) {
5266                         if (tabular.ltCaption(r)) {
5267                                 cur.idx() = tabular.cellIndex(r, 0);
5268                                 cur.pit() = 0;
5269                                 cur.pos() = 0;
5270                                 tabularFeatures(cur, Tabular::TOGGLE_LTCAPTION);
5271                         }
5272                 }
5273                 tabular.is_long_tabular = false;
5274                 break;
5275
5276         case Tabular::SET_ROTATE_TABULAR:
5277                 tabular.rotate = true;
5278                 break;
5279
5280         case Tabular::UNSET_ROTATE_TABULAR:
5281                 tabular.rotate = false;
5282                 break;
5283
5284         case Tabular::TOGGLE_ROTATE_TABULAR:
5285                 tabular.rotate = !tabular.rotate;
5286                 break;
5287
5288         case Tabular::TABULAR_VALIGN_TOP:
5289                 tabular.tabular_valignment = Tabular::LYX_VALIGN_TOP;
5290                 break;
5291
5292         case Tabular::TABULAR_VALIGN_MIDDLE:
5293                 tabular.tabular_valignment = Tabular::LYX_VALIGN_MIDDLE;
5294                 break;
5295
5296         case Tabular::TABULAR_VALIGN_BOTTOM:
5297                 tabular.tabular_valignment = Tabular::LYX_VALIGN_BOTTOM;
5298                 break;
5299
5300         case Tabular::LONGTABULAR_ALIGN_LEFT:
5301                 tabular.longtabular_alignment = Tabular::LYX_LONGTABULAR_ALIGN_LEFT;
5302                 break;
5303
5304         case Tabular::LONGTABULAR_ALIGN_CENTER:
5305                 tabular.longtabular_alignment = Tabular::LYX_LONGTABULAR_ALIGN_CENTER;
5306                 break;
5307
5308         case Tabular::LONGTABULAR_ALIGN_RIGHT:
5309                 tabular.longtabular_alignment = Tabular::LYX_LONGTABULAR_ALIGN_RIGHT;
5310                 break;
5311
5312                 
5313
5314         case Tabular::SET_ROTATE_CELL:
5315                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5316                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5317                                 tabular.setRotateCell(tabular.cellIndex(r, c), true);
5318                 break;
5319
5320         case Tabular::UNSET_ROTATE_CELL:
5321                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5322                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5323                                 tabular.setRotateCell(tabular.cellIndex(r, c), false);
5324                 break;
5325
5326         case Tabular::TOGGLE_ROTATE_CELL:
5327                 {
5328                 bool oneNotRotated = oneCellHasRotationState(false,
5329                         sel_row_start, sel_row_end, sel_col_start, sel_col_end);
5330
5331                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5332                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5333                                 tabular.setRotateCell(tabular.cellIndex(r, c),
5334                                                                           oneNotRotated);
5335                 }
5336                 break;
5337
5338         case Tabular::SET_USEBOX: {
5339                 Tabular::BoxType val = Tabular::BoxType(convert<int>(value));
5340                 if (val == tabular.getUsebox(cur.idx()))
5341                         val = Tabular::BOX_NONE;
5342                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5343                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5344                                 tabular.setUsebox(tabular.cellIndex(r, c), val);
5345                 break;
5346         }
5347
5348         case Tabular::UNSET_LTFIRSTHEAD:
5349                 flag = false;
5350         case Tabular::SET_LTFIRSTHEAD:
5351                 tabular.getRowOfLTFirstHead(row, ltt);
5352                 checkLongtableSpecial(ltt, value, flag);
5353                 tabular.setLTHead(row, flag, ltt, true);
5354                 break;
5355
5356         case Tabular::UNSET_LTHEAD:
5357                 flag = false;
5358         case Tabular::SET_LTHEAD:
5359                 tabular.getRowOfLTHead(row, ltt);
5360                 checkLongtableSpecial(ltt, value, flag);
5361                 tabular.setLTHead(row, flag, ltt, false);
5362                 break;
5363
5364         case Tabular::UNSET_LTFOOT:
5365                 flag = false;
5366         case Tabular::SET_LTFOOT:
5367                 tabular.getRowOfLTFoot(row, ltt);
5368                 checkLongtableSpecial(ltt, value, flag);
5369                 tabular.setLTFoot(row, flag, ltt, false);
5370                 break;
5371
5372         case Tabular::UNSET_LTLASTFOOT:
5373                 flag = false;
5374         case Tabular::SET_LTLASTFOOT:
5375                 tabular.getRowOfLTLastFoot(row, ltt);
5376                 checkLongtableSpecial(ltt, value, flag);
5377                 tabular.setLTFoot(row, flag, ltt, true);
5378                 break;
5379
5380         case Tabular::SET_LTNEWPAGE:
5381                 tabular.setLTNewPage(row, !tabular.getLTNewPage(row));
5382                 break;
5383
5384         case Tabular::SET_LTCAPTION: {
5385                 if (tabular.ltCaption(row))
5386                         break;
5387                 cur.idx() = tabular.setLTCaption(row, true);
5388                 cur.pit() = 0;
5389                 cur.pos() = 0;
5390                 cur.setSelection(false);
5391                 // If a row is set as caption, then also insert
5392                 // a caption. Otherwise the LaTeX output is broken.
5393                 lyx::dispatch(FuncRequest(LFUN_INSET_SELECT_ALL));
5394                 lyx::dispatch(FuncRequest(LFUN_CAPTION_INSERT));
5395                 break;
5396         }
5397         
5398         case Tabular::UNSET_LTCAPTION: {
5399                 if (!tabular.ltCaption(row))
5400                         break;
5401                 cur.idx() = tabular.setLTCaption(row, false);
5402                 cur.pit() = 0;
5403                 cur.pos() = 0;
5404                 cur.setSelection(false);
5405                 FuncRequest fr(LFUN_INSET_DISSOLVE, "caption");
5406                 if (lyx::getStatus(fr).enabled())
5407                         lyx::dispatch(fr);
5408                 break;
5409         }
5410
5411         case Tabular::TOGGLE_LTCAPTION: {
5412                 if (tabular.ltCaption(row))
5413                         tabularFeatures(cur, Tabular::UNSET_LTCAPTION);
5414                 else
5415                         tabularFeatures(cur, Tabular::SET_LTCAPTION);
5416                 break;
5417         }
5418
5419         case Tabular::SET_BOOKTABS:
5420                 tabular.use_booktabs = true;
5421                 break;
5422
5423         case Tabular::UNSET_BOOKTABS:
5424                 tabular.use_booktabs = false;
5425                 break;
5426
5427         case Tabular::SET_TOP_SPACE: {
5428                 Length len;
5429                 if (value == "default")
5430                         for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5431                                 tabular.row_info[r].top_space_default = true;
5432                 else if (value == "none")
5433                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
5434                                 tabular.row_info[r].top_space_default = false;
5435                                 tabular.row_info[r].top_space = len;
5436                         }
5437                 else if (isValidLength(value, &len))
5438                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
5439                                 tabular.row_info[r].top_space_default = false;
5440                                 tabular.row_info[r].top_space = len;
5441                         }
5442                 break;
5443         }
5444
5445         case Tabular::SET_BOTTOM_SPACE: {
5446                 Length len;
5447                 if (value == "default")
5448                         for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5449                                 tabular.row_info[r].bottom_space_default = true;
5450                 else if (value == "none")
5451                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
5452                                 tabular.row_info[r].bottom_space_default = false;
5453                                 tabular.row_info[r].bottom_space = len;
5454                         }
5455                 else if (isValidLength(value, &len))
5456                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
5457                                 tabular.row_info[r].bottom_space_default = false;
5458                                 tabular.row_info[r].bottom_space = len;
5459                         }
5460                 break;
5461         }
5462
5463         case Tabular::SET_INTERLINE_SPACE: {
5464                 Length len;
5465                 if (value == "default")
5466                         for (row_type r = sel_row_start; r <= sel_row_end; ++r)
5467                                 tabular.row_info[r].interline_space_default = true;
5468                 else if (value == "none")
5469                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
5470                                 tabular.row_info[r].interline_space_default = false;
5471                                 tabular.row_info[r].interline_space = len;
5472                         }
5473                 else if (isValidLength(value, &len))
5474                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
5475                                 tabular.row_info[r].interline_space_default = false;
5476                                 tabular.row_info[r].interline_space = len;
5477                         }
5478                 break;
5479         }
5480
5481         case Tabular::SET_DECIMAL_POINT:
5482                 for (col_type c = sel_col_start; c <= sel_col_end; ++c)
5483                         tabular.column_info[c].decimal_point = from_utf8(value);
5484                 break;
5485
5486         // dummy stuff just to avoid warnings
5487         case Tabular::LAST_ACTION:
5488                 break;
5489         }
5490 }
5491
5492
5493 bool InsetTabular::copySelection(Cursor & cur)
5494 {
5495         if (!cur.selection())
5496                 return false;
5497
5498         row_type rs, re;
5499         col_type cs, ce;
5500         getSelection(cur, rs, re, cs, ce);
5501
5502         paste_tabular.reset(new Tabular(tabular));
5503
5504         for (row_type r = 0; r < rs; ++r)
5505                 paste_tabular->deleteRow(0);
5506
5507         row_type const rows = re - rs + 1;
5508         while (paste_tabular->nrows() > rows)
5509                 paste_tabular->deleteRow(rows);
5510
5511         for (col_type c = 0; c < cs; ++c)
5512                 paste_tabular->deleteColumn(0);
5513
5514         col_type const columns = ce - cs + 1;
5515         while (paste_tabular->ncols() > columns)
5516                 paste_tabular->deleteColumn(columns);
5517
5518         paste_tabular->setBuffer(tabular.buffer());
5519
5520         odocstringstream os;
5521         OutputParams const runparams(0);
5522         paste_tabular->plaintext(os, runparams, 0, true, '\t');
5523         // Needed for the "Edit->Paste recent" menu and the system clipboard.
5524         cap::copySelection(cur, os.str());
5525
5526         // mark tabular stack dirty
5527         // FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
5528         // when we (hopefully) have a one-for-all paste mechanism.
5529         // This must be called after cap::copySelection.
5530         dirtyTabularStack(true);
5531
5532         return true;
5533 }
5534
5535
5536 bool InsetTabular::pasteClipboard(Cursor & cur)
5537 {
5538         if (!paste_tabular)
5539                 return false;
5540         col_type actcol = tabular.cellColumn(cur.idx());
5541         row_type actrow = tabular.cellRow(cur.idx());
5542
5543         if (cur.selIsMultiCell()) {
5544                 row_type re;
5545                 col_type ce;
5546                 getSelection(cur, actrow, re, actcol, ce);
5547         }
5548
5549         for (row_type r1 = 0, r2 = actrow;
5550              r1 < paste_tabular->nrows() && r2 < tabular.nrows();
5551              ++r1, ++r2) {
5552                 for (col_type c1 = 0, c2 = actcol;
5553                     c1 < paste_tabular->ncols() && c2 < tabular.ncols();
5554                     ++c1, ++c2) {
5555                         if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
5556                               tabular.isPartOfMultiColumn(r2, c2))
5557                                 continue;
5558                         if (paste_tabular->isPartOfMultiColumn(r1, c1)) {
5559                                 --c2;
5560                                 continue;
5561                         }
5562                         if (tabular.isPartOfMultiColumn(r2, c2)) {
5563                                 --c1;
5564                                 continue;
5565                         }
5566                         shared_ptr<InsetTableCell> inset(
5567                                 new InsetTableCell(*paste_tabular->cellInset(r1, c1)));
5568                         tabular.setCellInset(r2, c2, inset);
5569                         // FIXME?: why do we need to do this explicitly? (EL)
5570                         tabular.cellInset(r2, c2)->setBuffer(tabular.buffer());
5571
5572                         // FIXME: change tracking (MG)
5573                         inset->setChange(Change(buffer().params().trackChanges ?
5574                                                 Change::INSERTED : Change::UNCHANGED));
5575                         cur.pos() = 0;
5576                 }
5577         }
5578         return true;
5579 }
5580
5581
5582 void InsetTabular::cutSelection(Cursor & cur)
5583 {
5584         if (!cur.selection())
5585                 return;
5586
5587         row_type rs, re;
5588         col_type cs, ce;
5589         getSelection(cur, rs, re, cs, ce);
5590         for (row_type r = rs; r <= re; ++r) {
5591                 for (col_type c = cs; c <= ce; ++c) {
5592                         shared_ptr<InsetTableCell> t
5593                                 = cell(tabular.cellIndex(r, c));
5594                         if (buffer().params().trackChanges)
5595                                 // FIXME: Change tracking (MG)
5596                                 t->setChange(Change(Change::DELETED));
5597                         else
5598                                 t->clear();
5599                 }
5600         }
5601
5602         // cursor position might be invalid now
5603         if (cur.pit() > cur.lastpit())
5604                 cur.pit() = cur.lastpit();
5605         if (cur.pos() > cur.lastpos())
5606                 cur.pos() = cur.lastpos();
5607         cur.clearSelection();
5608 }
5609
5610
5611 bool InsetTabular::isRightToLeft(Cursor & cur) const
5612 {
5613         LASSERT(cur.depth() > 1, /**/);
5614         Paragraph const & parentpar = cur[cur.depth() - 2].paragraph();
5615         pos_type const parentpos = cur[cur.depth() - 2].pos();
5616         return parentpar.getFontSettings(buffer().params(),
5617                                          parentpos).language()->rightToLeft();
5618 }
5619
5620
5621 docstring InsetTabular::asString(idx_type stidx, idx_type enidx, 
5622                                  bool intoInsets)
5623 {
5624         LASSERT(stidx <= enidx, return docstring());
5625         docstring retval;
5626         col_type const col1 = tabular.cellColumn(stidx);
5627         col_type const col2 = tabular.cellColumn(enidx);
5628         row_type const row1 = tabular.cellRow(stidx);
5629         row_type const row2 = tabular.cellRow(enidx);
5630         bool first = true;
5631         for (col_type col = col1; col <= col2; col++)
5632                 for (row_type row = row1; row <= row2; row++) {
5633                         if (!first)
5634                                 retval += "\n";
5635                         else
5636                                 first = false;
5637                         retval += tabular.cellInset(row, col)->asString(intoInsets);
5638                 }
5639         return retval;
5640 }
5641
5642
5643 void InsetTabular::getSelection(Cursor & cur,
5644         row_type & rs, row_type & re, col_type & cs, col_type & ce) const
5645 {
5646         CursorSlice const & beg = cur.selBegin();
5647         CursorSlice const & end = cur.selEnd();
5648         cs = tabular.cellColumn(beg.idx());
5649         ce = tabular.cellColumn(end.idx());
5650         if (cs > ce)
5651                 swap(cs, ce);
5652
5653         rs = tabular.cellRow(beg.idx());
5654         re = tabular.cellRow(end.idx());
5655         if (rs > re)
5656                 swap(rs, re);
5657 }
5658
5659
5660 Text * InsetTabular::getText(int idx) const
5661 {
5662         return size_t(idx) < nargs() ? cell(idx)->getText(0) : 0;
5663 }
5664
5665
5666 void InsetTabular::setChange(Change const & change)
5667 {
5668         for (idx_type idx = 0; idx < nargs(); ++idx)
5669                 cell(idx)->setChange(change);
5670 }
5671
5672
5673 void InsetTabular::acceptChanges()
5674 {
5675         for (idx_type idx = 0; idx < nargs(); ++idx)
5676                 cell(idx)->acceptChanges();
5677 }
5678
5679
5680 void InsetTabular::rejectChanges()
5681 {
5682         for (idx_type idx = 0; idx < nargs(); ++idx)
5683                 cell(idx)->rejectChanges();
5684 }
5685
5686
5687 bool InsetTabular::allowParagraphCustomization(idx_type cell) const
5688 {
5689         return tabular.getPWidth(cell).zero();
5690 }
5691
5692
5693 bool InsetTabular::forcePlainLayout(idx_type cell) const
5694 {
5695         return !tabular.getPWidth(cell).zero();
5696 }
5697
5698
5699 bool InsetTabular::insertPlaintextString(BufferView & bv, docstring const & buf,
5700                                      bool usePaste)
5701 {
5702         if (buf.length() <= 0)
5703                 return true;
5704
5705         col_type cols = 1;
5706         row_type rows = 1;
5707         col_type maxCols = 1;
5708         size_t const len = buf.length();
5709         size_t p = 0;
5710
5711         while (p < len &&
5712                (p = buf.find_first_of(from_ascii("\t\n"), p)) != docstring::npos) {
5713                 switch (buf[p]) {
5714                 case '\t':
5715                         ++cols;
5716                         break;
5717                 case '\n':
5718                         if (p + 1 < len)
5719                                 ++rows;
5720                         maxCols = max(cols, maxCols);
5721                         cols = 1;
5722                         break;
5723                 }
5724                 ++p;
5725         }
5726         maxCols = max(cols, maxCols);
5727         Tabular * loctab;
5728         idx_type cell = 0;
5729         col_type ocol = 0;
5730         row_type row = 0;
5731         if (usePaste) {
5732                 paste_tabular.reset(new Tabular(buffer_, rows, maxCols));
5733                 loctab = paste_tabular.get();
5734                 cols = 0;
5735                 dirtyTabularStack(true);
5736         } else {
5737                 loctab = &tabular;
5738                 cell = bv.cursor().idx();
5739                 ocol = tabular.cellColumn(cell);
5740                 row = tabular.cellRow(cell);
5741         }
5742
5743         size_t op = 0;
5744         idx_type const cells = loctab->numberofcells;
5745         p = 0;
5746         cols = ocol;
5747         rows = loctab->nrows();
5748         col_type const columns = loctab->ncols();
5749
5750         while (cell < cells && p < len && row < rows &&
5751                (p = buf.find_first_of(from_ascii("\t\n"), p)) != docstring::npos)
5752         {
5753                 if (p >= len)
5754                         break;
5755                 switch (buf[p]) {
5756                 case '\t':
5757                         // we can only set this if we are not too far right
5758                         if (cols < columns) {
5759                                 shared_ptr<InsetTableCell> inset = loctab->cellInset(cell);
5760                                 Font const font = bv.textMetrics(&inset->text()).
5761                                         displayFont(0, 0);
5762                                 inset->setText(buf.substr(op, p - op), font,
5763                                                buffer().params().trackChanges);
5764                                 ++cols;
5765                                 ++cell;
5766                         }
5767                         break;
5768                 case '\n':
5769                         // we can only set this if we are not too far right
5770                         if (cols < columns) {
5771                                 shared_ptr<InsetTableCell> inset = tabular.cellInset(cell);
5772                                 Font const font = bv.textMetrics(&inset->text()).
5773                                         displayFont(0, 0);
5774                                 inset->setText(buf.substr(op, p - op), font,
5775                                                buffer().params().trackChanges);
5776                         }
5777                         cols = ocol;
5778                         ++row;
5779                         if (row < rows)
5780                                 cell = loctab->cellIndex(row, cols);
5781                         break;
5782                 }
5783                 ++p;
5784                 op = p;
5785         }
5786         // check for the last cell if there is no trailing '\n'
5787         if (cell < cells && op < len) {
5788                 shared_ptr<InsetTableCell> inset = loctab->cellInset(cell);
5789                 Font const font = bv.textMetrics(&inset->text()).displayFont(0, 0);
5790                 inset->setText(buf.substr(op, len - op), font,
5791                         buffer().params().trackChanges);
5792         }
5793         return true;
5794 }
5795
5796
5797 void InsetTabular::addPreview(DocIterator const & inset_pos,
5798         PreviewLoader & loader) const
5799 {
5800         DocIterator cell_pos = inset_pos;
5801
5802         cell_pos.push_back(CursorSlice(*const_cast<InsetTabular *>(this)));
5803         for (row_type r = 0; r < tabular.nrows(); ++r) {
5804                 for (col_type c = 0; c < tabular.ncols(); ++c) {
5805                         cell_pos.top().idx() = tabular.cellIndex(r, c);
5806                         tabular.cellInset(r, c)->addPreview(cell_pos, loader);
5807                 }
5808         }
5809 }
5810
5811
5812 bool InsetTabular::completionSupported(Cursor const & cur) const
5813 {
5814         Cursor const & bvCur = cur.bv().cursor();
5815         if (&bvCur.inset() != this)
5816                 return false;
5817         return cur.text()->completionSupported(cur);
5818 }
5819
5820
5821 bool InsetTabular::inlineCompletionSupported(Cursor const & cur) const
5822 {
5823         return completionSupported(cur);
5824 }
5825
5826
5827 bool InsetTabular::automaticInlineCompletion() const
5828 {
5829         return lyxrc.completion_inline_text;
5830 }
5831
5832
5833 bool InsetTabular::automaticPopupCompletion() const
5834 {
5835         return lyxrc.completion_popup_text;
5836 }
5837
5838
5839 bool InsetTabular::showCompletionCursor() const
5840 {
5841         return lyxrc.completion_cursor_text;
5842 }
5843
5844
5845 CompletionList const * InsetTabular::createCompletionList(Cursor const & cur) const
5846 {
5847         return completionSupported(cur) ? cur.text()->createCompletionList(cur) : 0;
5848 }
5849
5850
5851 docstring InsetTabular::completionPrefix(Cursor const & cur) const
5852 {
5853         if (!completionSupported(cur))
5854                 return docstring();
5855         return cur.text()->completionPrefix(cur);
5856 }
5857
5858
5859 bool InsetTabular::insertCompletion(Cursor & cur, docstring const & s, bool finished)
5860 {
5861         if (!completionSupported(cur))
5862                 return false;
5863
5864         return cur.text()->insertCompletion(cur, s, finished);
5865 }
5866
5867
5868 void InsetTabular::completionPosAndDim(Cursor const & cur, int & x, int & y, 
5869                                     Dimension & dim) const
5870 {
5871         TextMetrics const & tm = cur.bv().textMetrics(cur.text());
5872         tm.completionPosAndDim(cur, x, y, dim);
5873 }
5874
5875
5876 void InsetTabular::string2params(string const & in, InsetTabular & inset)
5877 {
5878         istringstream data(in);
5879         Lexer lex;
5880         lex.setStream(data);
5881
5882         if (in.empty())
5883                 return;
5884
5885         string token;
5886         lex >> token;
5887         if (!lex || token != "tabular") {
5888                 LYXERR0("Expected arg 1 to be \"tabular\" in " << in);
5889                 return;
5890         }
5891
5892         // This is part of the inset proper that is usually swallowed
5893         // by Buffer::readInset
5894         lex >> token;
5895         if (!lex || token != "Tabular") {
5896                 LYXERR0("Expected arg 2 to be \"Tabular\" in " << in);
5897                 return;
5898         }
5899
5900         inset.read(lex);
5901 }
5902
5903
5904 string InsetTabular::params2string(InsetTabular const & inset)
5905 {
5906         ostringstream data;
5907         data << "tabular" << ' ';
5908         inset.write(data);
5909         data << "\\end_inset\n";
5910         return data.str();
5911 }
5912
5913
5914 } // namespace lyx