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