]> git.lyx.org Git - lyx.git/blob - src/insets/InsetTabular.cpp
d52ba528a37c94fdb56ef922c4caaefd23719b21
[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  * \author Scott Kostyshak
17  *
18  * Full author contact details are available in file CREDITS.
19  */
20
21 #include <config.h>
22
23 #include "InsetTabular.h"
24
25 #include "buffer_funcs.h"
26 #include "Buffer.h"
27 #include "BufferParams.h"
28 #include "BufferView.h"
29 #include "CoordCache.h"
30 #include "Counters.h"
31 #include "Cursor.h"
32 #include "CutAndPaste.h"
33 #include "DispatchResult.h"
34 #include "FuncRequest.h"
35 #include "FuncStatus.h"
36 #include "InsetIterator.h"
37 #include "InsetList.h"
38 #include "Language.h"
39 #include "LaTeXFeatures.h"
40 #include "Lexer.h"
41 #include "LyX.h"
42 #include "LyXRC.h"
43 #include "MetricsInfo.h"
44 #include "OutputParams.h"
45 #include "output_xhtml.h"
46 #include "Paragraph.h"
47 #include "ParagraphParameters.h"
48 #include "ParIterator.h"
49 #include "TexRow.h"
50 #include "texstream.h"
51 #include "TextClass.h"
52 #include "TextMetrics.h"
53
54 #include "frontends/Application.h"
55 #include "frontends/alert.h"
56 #include "frontends/Clipboard.h"
57 #include "frontends/Painter.h"
58 #include "frontends/Selection.h"
59
60 #include "support/convert.h"
61 #include "support/debug.h"
62 #include "support/docstream.h"
63 #include "support/FileName.h"
64 #include "support/gettext.h"
65 #include "support/lassert.h"
66 #include "support/lstrings.h"
67 #include "support/unique_ptr.h"
68
69 #include <cstring>
70 #include <iomanip>
71 #include <iostream>
72 #include <limits>
73 #include <sstream>
74
75 using namespace std;
76 using namespace lyx::support;
77
78
79
80 namespace lyx {
81
82 using cap::dirtyTabularStack;
83 using cap::tabularStackDirty;
84
85 using graphics::PreviewLoader;
86
87 using frontend::Painter;
88 using frontend::Clipboard;
89
90 namespace Alert = frontend::Alert;
91
92
93 namespace {
94
95 int const ADD_TO_HEIGHT = 2; // in cell
96 int const ADD_TO_TABULAR_WIDTH = 6; // horizontal space before and after the table
97 int const default_line_space = 10; // ?
98 int const WIDTH_OF_LINE = 5; // space between double lines
99
100
101 ///
102 unique_ptr<Tabular> paste_tabular;
103
104
105 struct TabularFeature {
106         Tabular::Feature action;
107         string feature;
108         bool need_value;
109 };
110
111
112 TabularFeature tabularFeature[] =
113 {
114         // the SET/UNSET actions are used by the table dialog,
115         // the TOGGLE actions by the table toolbar buttons
116         // FIXME: these values have been hardcoded in InsetMathGrid and other
117         // math insets.
118         { Tabular::APPEND_ROW, "append-row", false },
119         { Tabular::APPEND_COLUMN, "append-column", false },
120         { Tabular::DELETE_ROW, "delete-row", false },
121         { Tabular::DELETE_COLUMN, "delete-column", false },
122         { Tabular::COPY_ROW, "copy-row", false },
123         { Tabular::COPY_COLUMN, "copy-column", false },
124         { Tabular::MOVE_COLUMN_RIGHT, "move-column-right", false },
125         { Tabular::MOVE_COLUMN_LEFT, "move-column-left", false },
126         { Tabular::MOVE_ROW_DOWN, "move-row-down", false },
127         { Tabular::MOVE_ROW_UP, "move-row-up", false },
128         { Tabular::SET_LINE_TOP, "set-line-top", true },
129         { Tabular::SET_LINE_BOTTOM, "set-line-bottom", true },
130         { Tabular::SET_LTRIM_TOP, "set-ltrim-top", true },
131         { Tabular::SET_LTRIM_BOTTOM, "set-ltrim-bottom", true },
132         { Tabular::SET_RTRIM_TOP, "set-rtrim-top", true },
133         { Tabular::SET_RTRIM_BOTTOM, "set-rtrim-bottom", true },
134         { Tabular::SET_LINE_LEFT, "set-line-left", true },
135         { Tabular::SET_LINE_RIGHT, "set-line-right", true },
136         { Tabular::TOGGLE_LINE_TOP, "toggle-line-top", false },
137         { Tabular::TOGGLE_LINE_BOTTOM, "toggle-line-bottom", false },
138         { Tabular::TOGGLE_LINE_LEFT, "toggle-line-left", false },
139         { Tabular::TOGGLE_LINE_RIGHT, "toggle-line-right", false },
140         { Tabular::TOGGLE_LTRIM_TOP, "toggle-ltrim-top", false },
141         { Tabular::TOGGLE_LTRIM_BOTTOM, "toggle-ltrim-bottom", false },
142         { Tabular::TOGGLE_RTRIM_TOP, "toggle-rtrim-top", false },
143         { Tabular::TOGGLE_RTRIM_BOTTOM, "toggle-rtrim-bottom", false },
144         { Tabular::ALIGN_LEFT, "align-left", false },
145         { Tabular::ALIGN_RIGHT, "align-right", false },
146         { Tabular::ALIGN_CENTER, "align-center", false },
147         { Tabular::ALIGN_BLOCK, "align-block", false },
148         { Tabular::ALIGN_DECIMAL, "align-decimal", false },
149         { Tabular::VALIGN_TOP, "valign-top", false },
150         { Tabular::VALIGN_BOTTOM, "valign-bottom", false },
151         { Tabular::VALIGN_MIDDLE, "valign-middle", false },
152         { Tabular::M_ALIGN_LEFT, "m-align-left", false },
153         { Tabular::M_ALIGN_RIGHT, "m-align-right", false },
154         { Tabular::M_ALIGN_CENTER, "m-align-center", false },
155         { Tabular::M_VALIGN_TOP, "m-valign-top", false },
156         { Tabular::M_VALIGN_BOTTOM, "m-valign-bottom", false },
157         { Tabular::M_VALIGN_MIDDLE, "m-valign-middle", false },
158         { Tabular::MULTICOLUMN, "multicolumn", false },
159         { Tabular::SET_MULTICOLUMN, "set-multicolumn", false },
160         { Tabular::UNSET_MULTICOLUMN, "unset-multicolumn", false },
161         { Tabular::MULTIROW, "multirow", false },
162         { Tabular::SET_MULTIROW, "set-multirow", false },
163         { Tabular::UNSET_MULTIROW, "unset-multirow", false },
164         { Tabular::SET_MROFFSET, "set-mroffset", true },
165         { Tabular::SET_ALL_LINES, "set-all-lines", false },
166         { Tabular::RESET_FORMAL_DEFAULT, "reset-formal-default", false },
167         { Tabular::UNSET_ALL_LINES, "unset-all-lines", false },
168         { Tabular::TOGGLE_LONGTABULAR, "toggle-longtabular", false },
169         { Tabular::SET_LONGTABULAR, "set-longtabular", false },
170         { Tabular::UNSET_LONGTABULAR, "unset-longtabular", false },
171         { Tabular::SET_PWIDTH, "set-pwidth", true },
172         { Tabular::SET_MPWIDTH, "set-mpwidth", true },
173         { Tabular::TOGGLE_VARWIDTH_COLUMN, "toggle-varwidth-column", true },
174         { Tabular::SET_ROTATE_TABULAR, "set-rotate-tabular", true },
175         { Tabular::UNSET_ROTATE_TABULAR, "unset-rotate-tabular", true },
176         { Tabular::TOGGLE_ROTATE_TABULAR, "toggle-rotate-tabular", true },
177         { Tabular::SET_ROTATE_CELL, "set-rotate-cell", true },
178         { Tabular::UNSET_ROTATE_CELL, "unset-rotate-cell", true },
179         { Tabular::TOGGLE_ROTATE_CELL, "toggle-rotate-cell", true },
180         { Tabular::SET_USEBOX, "set-usebox", true },
181         { Tabular::SET_LTHEAD, "set-lthead", true },
182         { Tabular::UNSET_LTHEAD, "unset-lthead", true },
183         { Tabular::SET_LTFIRSTHEAD, "set-ltfirsthead", true },
184         { Tabular::UNSET_LTFIRSTHEAD, "unset-ltfirsthead", true },
185         { Tabular::SET_LTFOOT, "set-ltfoot", true },
186         { Tabular::UNSET_LTFOOT, "unset-ltfoot", true },
187         { Tabular::SET_LTLASTFOOT, "set-ltlastfoot", true },
188         { Tabular::UNSET_LTLASTFOOT, "unset-ltlastfoot", true },
189         { Tabular::SET_LTNEWPAGE, "set-ltnewpage", false },
190         { Tabular::UNSET_LTNEWPAGE, "unset-ltnewpage", false },
191         { Tabular::TOGGLE_LTCAPTION, "toggle-ltcaption", false },
192         { Tabular::SET_LTCAPTION, "set-ltcaption", false },
193         { Tabular::UNSET_LTCAPTION, "unset-ltcaption", false },
194         { Tabular::SET_SPECIAL_COLUMN, "set-special-column", true },
195         { Tabular::SET_SPECIAL_MULTICOLUMN, "set-special-multicolumn", true },
196         { Tabular::TOGGLE_BOOKTABS, "toggle-booktabs", false },
197         { Tabular::SET_BOOKTABS, "set-booktabs", false },
198         { Tabular::UNSET_BOOKTABS, "unset-booktabs", false },
199         { Tabular::SET_TOP_SPACE, "set-top-space", true },
200         { Tabular::SET_BOTTOM_SPACE, "set-bottom-space", true },
201         { Tabular::SET_INTERLINE_SPACE, "set-interline-space", true },
202         { Tabular::SET_BORDER_LINES, "set-border-lines", false },
203         { Tabular::TABULAR_VALIGN_TOP, "tabular-valign-top", false},
204         { Tabular::TABULAR_VALIGN_MIDDLE, "tabular-valign-middle", false},
205         { Tabular::TABULAR_VALIGN_BOTTOM, "tabular-valign-bottom", false},
206         { Tabular::LONGTABULAR_ALIGN_LEFT, "longtabular-align-left", false },
207         { Tabular::LONGTABULAR_ALIGN_CENTER, "longtabular-align-center", false },
208         { Tabular::LONGTABULAR_ALIGN_RIGHT, "longtabular-align-right", false },
209         { Tabular::SET_DECIMAL_POINT, "set-decimal-point", true },
210         { Tabular::SET_TABULAR_WIDTH, "set-tabular-width", true },
211         { Tabular::SET_INNER_LINES, "set-inner-lines", false },
212         { Tabular::LAST_ACTION, "", false }
213 };
214
215
216 string const tostr(LyXAlignment const & num)
217 {
218         switch (num) {
219         case LYX_ALIGN_NONE:
220                 return "none";
221         case LYX_ALIGN_BLOCK:
222                 return "block";
223         case LYX_ALIGN_LEFT:
224                 return "left";
225         case LYX_ALIGN_CENTER:
226                 return "center";
227         case LYX_ALIGN_RIGHT:
228                 return "right";
229         case LYX_ALIGN_LAYOUT:
230                 return "layout";
231         case LYX_ALIGN_SPECIAL:
232                 return "special";
233         case LYX_ALIGN_DECIMAL:
234                 return "decimal";
235         }
236         return string();
237 }
238
239
240 string const tostr(Tabular::HAlignment const & num)
241 {
242         switch (num) {
243         case Tabular::LYX_LONGTABULAR_ALIGN_LEFT:
244                 return "left";
245         case Tabular::LYX_LONGTABULAR_ALIGN_CENTER:
246                 return "center";
247         case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT:
248                 return "right";
249         }
250         return string();
251 }
252
253
254 string const tostr(Tabular::VAlignment const & num)
255 {
256         switch (num) {
257         case Tabular::LYX_VALIGN_TOP:
258                 return "top";
259         case Tabular::LYX_VALIGN_MIDDLE:
260                 return "middle";
261         case Tabular::LYX_VALIGN_BOTTOM:
262                 return "bottom";
263         }
264         return string();
265 }
266
267
268 string const tostr(Tabular::BoxType const & num)
269 {
270         switch (num) {
271         case Tabular::BOX_NONE:
272                 return "none";
273         case Tabular::BOX_PARBOX:
274                 return "parbox";
275         case Tabular::BOX_MINIPAGE:
276                 return "minipage";
277         case Tabular::BOX_VARWIDTH:
278                 return "varwidth";
279         }
280         return string();
281 }
282
283
284 // I would have liked a fromstr template a lot better. (Lgb)
285 bool string2type(string const & str, LyXAlignment & num)
286 {
287         if (str == "none")
288                 num = LYX_ALIGN_NONE;
289         else if (str == "block")
290                 num = LYX_ALIGN_BLOCK;
291         else if (str == "left")
292                 num = LYX_ALIGN_LEFT;
293         else if (str == "center")
294                 num = LYX_ALIGN_CENTER;
295         else if (str == "right")
296                 num = LYX_ALIGN_RIGHT;
297         else if (str == "decimal")
298                 num = LYX_ALIGN_DECIMAL;
299         else
300                 return false;
301         return true;
302 }
303
304
305 bool string2type(string const & str, Tabular::HAlignment & num)
306 {
307         if (str == "left")
308                 num = Tabular::LYX_LONGTABULAR_ALIGN_LEFT;
309         else if (str == "center" )
310                 num = Tabular::LYX_LONGTABULAR_ALIGN_CENTER;
311         else if (str == "right")
312                 num = Tabular::LYX_LONGTABULAR_ALIGN_RIGHT;
313         else
314                 return false;
315         return true;
316 }
317
318
319 bool string2type(string const & str, Tabular::VAlignment & num)
320 {
321         if (str == "top")
322                 num = Tabular::LYX_VALIGN_TOP;
323         else if (str == "middle" )
324                 num = Tabular::LYX_VALIGN_MIDDLE;
325         else if (str == "bottom")
326                 num = Tabular::LYX_VALIGN_BOTTOM;
327         else
328                 return false;
329         return true;
330 }
331
332
333 bool string2type(string const & str, Tabular::BoxType & num)
334 {
335         if (str == "none")
336                 num = Tabular::BOX_NONE;
337         else if (str == "parbox")
338                 num = Tabular::BOX_PARBOX;
339         else if (str == "minipage")
340                 num = Tabular::BOX_MINIPAGE;
341         else if (str == "varwidth")
342                 num = Tabular::BOX_VARWIDTH;
343         else
344                 return false;
345         return true;
346 }
347
348
349 bool string2type(string const & str, bool & num)
350 {
351         if (str == "true")
352                 num = true;
353         else if (str == "false")
354                 num = false;
355         else
356                 return false;
357         return true;
358 }
359
360
361 bool getTokenValue(string const & str, char const * token, string & ret)
362 {
363         ret.erase();
364         size_t token_length = strlen(token);
365         size_t pos = str.find(token);
366
367         if (pos == string::npos || pos + token_length + 1 >= str.length()
368                 || str[pos + token_length] != '=')
369                 return false;
370         pos += token_length + 1;
371         char ch = str[pos];
372         if (ch != '"' && ch != '\'') { // only read till next space
373                 ret += ch;
374                 ch = ' ';
375         }
376         while (pos < str.length() - 1 && str[++pos] != ch)
377                 ret += str[pos];
378
379         return true;
380 }
381
382
383 bool getTokenValue(string const & str, char const * token, docstring & ret)
384 {
385         string tmp;
386         bool const success = getTokenValue(str, token, tmp);
387         ret = from_utf8(tmp);
388         return success;
389 }
390
391
392 bool getTokenValue(string const & str, char const * token, int & num)
393 {
394         string tmp;
395         num = 0;
396         if (!getTokenValue(str, token, tmp))
397                 return false;
398         num = convert<int>(tmp);
399         return true;
400 }
401
402
403 bool getTokenValue(string const & str, char const * token, LyXAlignment & num)
404 {
405         string tmp;
406         return getTokenValue(str, token, tmp) && string2type(tmp, num);
407 }
408
409
410 bool getTokenValue(string const & str, char const * token,
411                                    Tabular::HAlignment & num)
412 {
413         string tmp;
414         return getTokenValue(str, token, tmp) && string2type(tmp, num);
415 }
416
417
418 bool getTokenValue(string const & str, char const * token,
419                                    Tabular::VAlignment & num)
420 {
421         string tmp;
422         return getTokenValue(str, token, tmp) && string2type(tmp, num);
423 }
424
425
426 bool getTokenValue(string const & str, char const * token,
427                                    Tabular::BoxType & 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, bool & flag)
435 {
436         // set the flag always to false as this should be the default for bools
437         // not in the file-format.
438         flag = false;
439         string tmp;
440         return getTokenValue(str, token, tmp) && string2type(tmp, flag);
441 }
442
443
444 bool getTokenValue(string const & str, char const * token, Length & len)
445 {
446         // set the length to be zero() as default as this it should be if not
447         // in the file format.
448         len = Length();
449         string tmp;
450         return getTokenValue(str, token, tmp) && isValidLength(tmp, &len);
451 }
452
453
454 bool getTokenValue(string const & str, char const * token, Length & len, bool & flag)
455 {
456         len = Length();
457         flag = false;
458         string tmp;
459         if (!getTokenValue(str, token, tmp))
460                 return false;
461         if (tmp == "default") {
462                 flag = true;
463                 return  true;
464         }
465         return isValidLength(tmp, &len);
466 }
467
468
469 void l_getline(istream & is, string & str)
470 {
471         str.erase();
472         while (str.empty()) {
473                 getline(is, str);
474                 if (!str.empty() && str[str.length() - 1] == '\r')
475                         str.erase(str.length() - 1);
476         }
477 }
478
479 template <class T>
480 string const write_attribute(string const & name, T const & t)
481 {
482         string const s = tostr(t);
483         return s.empty() ? s : " " + name + "=\"" + s + "\"";
484 }
485
486 template <>
487 string const write_attribute(string const & name, string const & t)
488 {
489         return t.empty() ? t : " " + name + "=\"" + t + "\"";
490 }
491
492
493 template <>
494 string const write_attribute(string const & name, docstring const & t)
495 {
496         return t.empty() ? string() : " " + name + "=\"" + to_utf8(t) + "\"";
497 }
498
499
500 template <>
501 string const write_attribute(string const & name, bool const & b)
502 {
503         // we write only true attribute values so we remove a bit of the
504         // file format bloat for tabulars.
505         return b ? write_attribute(name, convert<string>(b)) : string();
506 }
507
508
509 template <>
510 string const write_attribute(string const & name, int const & i)
511 {
512         // we write only true attribute values so we remove a bit of the
513         // file format bloat for tabulars.
514         return i ? write_attribute(name, convert<string>(i)) : string();
515 }
516
517
518 template <>
519 string const write_attribute(string const & name, Tabular::idx_type const & i)
520 {
521         // we write only true attribute values so we remove a bit of the
522         // file format bloat for tabulars.
523         return i ? write_attribute(name, convert<string>(i)) : string();
524 }
525
526
527 template <>
528 string const write_attribute(string const & name, Length const & value)
529 {
530         // we write only the value if we really have one same reson as above.
531         return value.zero() ? string() : write_attribute(name, value.asString());
532 }
533
534 } // namespace
535
536
537 string const featureAsString(Tabular::Feature action)
538 {
539         for (size_t i = 0; i != Tabular::LAST_ACTION; ++i) {
540                 if (tabularFeature[i].action == action)
541                         return tabularFeature[i].feature;
542         }
543         return string();
544 }
545
546
547 DocIterator separatorPos(InsetTableCell const * cell, docstring const & align_d)
548 {
549         DocIterator dit = doc_iterator_begin(&(cell->buffer()), cell);
550         for (; dit; dit.forwardChar())
551                 if (dit.inTexted() && dit.depth() == 1
552                         && dit.paragraph().find(align_d, false, false, dit.pos()))
553                         break;
554
555         return dit;
556 }
557
558
559 InsetTableCell splitCell(InsetTableCell & head, docstring const & align_d, bool & hassep)
560 {
561         InsetTableCell tail = InsetTableCell(head);
562         DocIterator const dit = separatorPos(&head, align_d);
563         hassep = (bool)dit;
564         if (hassep) {
565                 pit_type const psize = head.paragraphs().front().size();
566                 head.paragraphs().front().eraseChars(dit.pos(), psize, false);
567                 tail.paragraphs().front().eraseChars(0,
568                         dit.pos() < psize ? dit.pos() + 1 : psize, false);
569         }
570
571         return tail;
572 }
573
574
575 /////////////////////////////////////////////////////////////////////
576 //
577 // Tabular
578 //
579 /////////////////////////////////////////////////////////////////////
580
581
582 Tabular::CellData::CellData(Buffer * buf)
583         : cellno(0),
584           width(0),
585           multicolumn(Tabular::CELL_NORMAL),
586           multirow(Tabular::CELL_NORMAL),
587           alignment(LYX_ALIGN_CENTER),
588           valignment(LYX_VALIGN_TOP),
589           decimal_hoffset(0),
590           decimal_width(0),
591           voffset(0),
592           top_line(false),
593           bottom_line(false),
594           left_line(false),
595           right_line(false),
596           top_line_rtrimmed(false),
597           top_line_ltrimmed(false),
598           bottom_line_rtrimmed(false),
599           bottom_line_ltrimmed(false),
600           usebox(BOX_NONE),
601           rotate(0),
602           inset(new InsetTableCell(buf))
603 {
604         inset->setBuffer(*buf);
605 }
606
607
608 Tabular::CellData::CellData(CellData const & cs)
609         : cellno(cs.cellno),
610           width(cs.width),
611           multicolumn(cs.multicolumn),
612           multirow(cs.multirow),
613           mroffset(cs.mroffset),
614           alignment(cs.alignment),
615           valignment(cs.valignment),
616           decimal_hoffset(cs.decimal_hoffset),
617           decimal_width(cs.decimal_width),
618           voffset(cs.voffset),
619           top_line(cs.top_line),
620           bottom_line(cs.bottom_line),
621           left_line(cs.left_line),
622           right_line(cs.right_line),
623           top_line_rtrimmed(cs.top_line_rtrimmed),
624           top_line_ltrimmed(cs.top_line_ltrimmed),
625           bottom_line_rtrimmed(cs.bottom_line_rtrimmed),
626           bottom_line_ltrimmed(cs.bottom_line_ltrimmed),
627           usebox(cs.usebox),
628           rotate(cs.rotate),
629           align_special(cs.align_special),
630           p_width(cs.p_width),
631           inset(static_cast<InsetTableCell *>(cs.inset->clone()))
632 {
633 }
634
635 Tabular::CellData & Tabular::CellData::operator=(CellData const & cs)
636 {
637         if (&cs == this)
638                 return *this;
639         cellno = cs.cellno;
640         width = cs.width;
641         multicolumn = cs.multicolumn;
642         multirow = cs.multirow;
643         mroffset = cs.mroffset;
644         alignment = cs.alignment;
645         valignment = cs.valignment;
646         decimal_hoffset = cs.decimal_hoffset;
647         decimal_width = cs.decimal_width;
648         voffset = cs.voffset;
649         top_line = cs.top_line;
650         bottom_line = cs.bottom_line;
651         left_line = cs.left_line;
652         right_line = cs.right_line;
653         top_line_rtrimmed = cs.top_line_rtrimmed;
654         top_line_ltrimmed = cs.top_line_ltrimmed;
655         bottom_line_rtrimmed = cs.bottom_line_rtrimmed;
656         bottom_line_ltrimmed = cs.bottom_line_ltrimmed;
657         usebox = cs.usebox;
658         rotate = cs.rotate;
659         align_special = cs.align_special;
660         p_width = cs.p_width;
661         inset.reset(static_cast<InsetTableCell *>(cs.inset->clone()));
662         return *this;
663 }
664
665 Tabular::RowData::RowData()
666         : ascent(0),
667           descent(0),
668           top_space_default(false),
669           bottom_space_default(false),
670           interline_space_default(false),
671           endhead(false),
672           endfirsthead(false),
673           endfoot(false),
674           endlastfoot(false),
675           newpage(false),
676           caption(false)
677 {}
678
679
680 Tabular::ColumnData::ColumnData()
681         : alignment(LYX_ALIGN_CENTER),
682           valignment(LYX_VALIGN_TOP),
683           width(0),
684           varwidth(false)
685 {
686 }
687
688
689 Tabular::ltType::ltType()
690         : set(false), topDL(false),
691           bottomDL(false),
692           empty(false)
693 {}
694
695
696 Tabular::Tabular(Buffer * buffer, row_type rows_arg, col_type columns_arg)
697 {
698         init(buffer, rows_arg, columns_arg);
699 }
700
701
702 void Tabular::setBuffer(Buffer & buffer)
703 {
704         buffer_ = &buffer;
705         for (row_type i = 0; i < nrows(); ++i)
706                 for (col_type j = 0; j < ncols(); ++j)
707                         cell_info[i][j].inset->setBuffer(*buffer_);
708 }
709
710
711 // activates all lines and sets all widths to 0
712 void Tabular::init(Buffer * buf, row_type rows_arg,
713                       col_type columns_arg)
714 {
715         buffer_ = buf;
716         row_info = row_vector(rows_arg);
717         column_info = column_vector(columns_arg);
718         cell_info = cell_vvector(rows_arg, cell_vector(columns_arg, CellData(buf)));
719         row_info.reserve(10);
720         column_info.reserve(10);
721         cell_info.reserve(100);
722         updateIndexes();
723         is_long_tabular = false;
724         tabular_valignment = LYX_VALIGN_MIDDLE;
725         tabular_width = Length();
726         longtabular_alignment = LYX_LONGTABULAR_ALIGN_CENTER;
727         rotate = 0;
728         use_booktabs = false;
729         // set silly default lines
730         for (row_type r = 0; r < nrows(); ++r)
731                 for (col_type c = 0; c < ncols(); ++c) {
732                         cell_info[r][c].inset->setBuffer(*buffer_);
733                         cell_info[r][c].top_line = true;
734                         cell_info[r][c].left_line = true;
735                         cell_info[r][c].bottom_line = r == 0 || r == nrows() - 1;
736                         cell_info[r][c].right_line = c == ncols() - 1;
737                 }
738 }
739
740
741 void Tabular::deleteRow(row_type const row)
742 {
743         // Not allowed to delete last row
744         if (nrows() == 1)
745                 return;
746
747         for (col_type c = 0; c < ncols(); ++c) {
748                 // Care about multirow cells
749                 if (row + 1 < nrows() &&
750                     cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW &&
751                     cell_info[row + 1][c].multirow == CELL_PART_OF_MULTIROW) {
752                                 cell_info[row + 1][c].multirow = CELL_BEGIN_OF_MULTIROW;
753                 }
754         }
755         row_info.erase(row_info.begin() + row);
756         cell_info.erase(cell_info.begin() + row);
757         updateIndexes();
758 }
759
760
761 void Tabular::copyRow(row_type const row)
762 {
763         insertRow(row, true);
764 }
765
766
767 void Tabular::appendRow(row_type row)
768 {
769         insertRow(row, false);
770 }
771
772
773 void Tabular::insertRow(row_type const row, bool copy)
774 {
775         row_info.insert(row_info.begin() + row + 1, RowData(row_info[row]));
776         cell_info.insert(cell_info.begin() + row + 1,
777                 cell_vector(0, CellData(buffer_)));
778
779         for (col_type c = 0; c < ncols(); ++c) {
780                 cell_info[row + 1].insert(cell_info[row + 1].begin() + c,
781                         copy ? CellData(cell_info[row][c]) : CellData(buffer_));
782                 if (buffer().params().track_changes)
783                         cell_info[row + 1][c].inset->setChange(Change(Change::INSERTED));
784                 if (cell_info[row][c].multirow == CELL_BEGIN_OF_MULTIROW)
785                         cell_info[row + 1][c].multirow = CELL_PART_OF_MULTIROW;
786         }
787
788         updateIndexes();
789         for (col_type c = 0; c < ncols(); ++c) {
790                 if (isPartOfMultiRow(row, c))
791                         continue;
792                 // inherit line settings
793                 idx_type const i = cellIndex(row + 1, c);
794                 idx_type const j = cellIndex(row, c);
795                 setLeftLine(i, leftLine(j));
796                 setRightLine(i, rightLine(j));
797                 setTopLine(i, topLine(j));
798                 if (topLine(j) && bottomLine(j)) {
799                         setBottomLine(i, true);
800                         setBottomLine(j, false);
801                 }
802                 // mark track changes
803                 if (buffer().params().track_changes)
804                         cellInfo(i).inset->setChange(Change(Change::INSERTED));
805         }
806 }
807
808
809 void Tabular::moveColumn(col_type col, ColDirection direction)
810 {
811         if (direction == Tabular::LEFT)
812                 col = col - 1;
813
814         std::swap(column_info[col], column_info[col + 1]);
815
816         for (row_type r = 0; r < nrows(); ++r) {
817                 std::swap(cell_info[r][col], cell_info[r][col + 1]);
818                 std::swap(cell_info[r][col].left_line, cell_info[r][col + 1].left_line);
819                 std::swap(cell_info[r][col].right_line, cell_info[r][col + 1].right_line);
820
821                 idx_type const i = cellIndex(r, col);
822                 idx_type const j = cellIndex(r, col + 1);
823                 if (buffer().params().track_changes) {
824                         cellInfo(i).inset->setChange(Change(Change::INSERTED));
825                         cellInfo(j).inset->setChange(Change(Change::INSERTED));
826                 }
827         }
828         updateIndexes();
829 }
830
831
832 void Tabular::moveRow(row_type row, RowDirection direction)
833 {
834         if (direction == Tabular::UP)
835                 row = row - 1;
836
837         std::swap(row_info[row], row_info[row + 1]);
838
839         for (col_type c = 0; c < ncols(); ++c) {
840                 std::swap(cell_info[row][c], cell_info[row + 1][c]);
841                 std::swap(cell_info[row][c].top_line, cell_info[row + 1][c].top_line);
842                 std::swap(cell_info[row][c].bottom_line, cell_info[row + 1][c].bottom_line);
843
844                 idx_type const i = cellIndex(row, c);
845                 idx_type const j = cellIndex(row + 1, c);
846                 if (buffer().params().track_changes) {
847                         cellInfo(i).inset->setChange(Change(Change::INSERTED));
848                         cellInfo(j).inset->setChange(Change(Change::INSERTED));
849                 }
850         }
851         updateIndexes();
852 }
853
854
855 void Tabular::deleteColumn(col_type const col)
856 {
857         // Not allowed to delete last column
858         if (ncols() == 1)
859                 return;
860
861         for (row_type r = 0; r < nrows(); ++r) {
862                 // Care about multicolumn cells
863                 if (col + 1 < ncols() &&
864                     cell_info[r][col].multicolumn == CELL_BEGIN_OF_MULTICOLUMN &&
865                     cell_info[r][col + 1].multicolumn == CELL_PART_OF_MULTICOLUMN) {
866                                 cell_info[r][col + 1].multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
867                 }
868                 cell_info[r].erase(cell_info[r].begin() + col);
869         }
870         column_info.erase(column_info.begin() + col);
871         updateIndexes();
872 }
873
874
875 void Tabular::copyColumn(col_type const col)
876 {
877         insertColumn(col, true);
878 }
879
880
881 void Tabular::appendColumn(col_type col)
882 {
883         insertColumn(col, false);
884 }
885
886
887 void Tabular::insertColumn(col_type const col, bool copy)
888 {
889         BufferParams const & bp = buffer().params();
890         column_info.insert(column_info.begin() + col + 1, ColumnData(column_info[col]));
891
892         for (row_type r = 0; r < nrows(); ++r) {
893                 cell_info[r].insert(cell_info[r].begin() + col + 1,
894                         copy ? CellData(cell_info[r][col]) : CellData(buffer_));
895                 if (bp.track_changes)
896                         cell_info[r][col + 1].inset->setChange(Change(Change::INSERTED));
897                 if (cell_info[r][col].multicolumn == CELL_BEGIN_OF_MULTICOLUMN)
898                         cell_info[r][col + 1].multicolumn = CELL_PART_OF_MULTICOLUMN;
899         }
900         updateIndexes();
901         for (row_type r = 0; r < nrows(); ++r) {
902                 // inherit line settings
903                 idx_type const i = cellIndex(r, col + 1);
904                 idx_type const j = cellIndex(r, col);
905                 setBottomLine(i, bottomLine(j));
906                 setTopLine(i, topLine(j));
907                 setLeftLine(i, leftLine(j));
908                 setRightLine(i, rightLine(j));
909                 if (rightLine(i) && rightLine(j)) {
910                         setRightLine(j, false);
911                 }
912                 if (buffer().params().track_changes)
913                         cellInfo(i).inset->setChange(Change(Change::INSERTED));
914         }
915 }
916
917
918 void Tabular::updateIndexes()
919 {
920         setBuffer(buffer());
921         numberofcells = 0;
922         // reset cell number
923         for (row_type row = 0; row < nrows(); ++row)
924                 for (col_type column = 0; column < ncols(); ++column) {
925                         if (!isPartOfMultiColumn(row, column)
926                                 && !isPartOfMultiRow(row, column))
927                                 ++numberofcells;
928                         if (isPartOfMultiRow(row, column))
929                                 cell_info[row][column].cellno = cell_info[row - 1][column].cellno;
930                         else
931                                 cell_info[row][column].cellno = numberofcells - 1;
932                 }
933
934         rowofcell.resize(numberofcells);
935         columnofcell.resize(numberofcells);
936         idx_type i = 0;
937         // reset column and row of cells and update their width and alignment
938         for (row_type row = 0; row < nrows(); ++row)
939                 for (col_type column = 0; column < ncols(); ++column) {
940                         if (isPartOfMultiColumn(row, column)) {
941                                 cell_info[row][column].inset->toggleMultiCol(true);
942                                 continue;
943                         }
944                         cell_info[row][column].inset->toggleMultiCol(false);
945                         // columnofcell needs to be called before setting width and aligment
946                         // multirow cells inherit the width from the column width
947                         if (!isPartOfMultiRow(row, column)) {
948                                 columnofcell[i] = column;
949                                 rowofcell[i] = row;
950                         }
951                         setFixedWidth(row, column);
952                         if (isPartOfMultiRow(row, column)) {
953                                 cell_info[row][column].inset->toggleMultiRow(true);
954                                 continue;
955                         }
956                         cell_info[row][column].inset->toggleMultiRow(false);
957                         cell_info[row][column].inset->setContentAlignment(
958                                 getAlignment(cellIndex(row, column)));
959                         ++i;
960                 }
961 }
962
963
964 Tabular::idx_type Tabular::numberOfCellsInRow(row_type const row) const
965 {
966         idx_type result = 0;
967         for (col_type c = 0; c < ncols(); ++c)
968                 if (cell_info[row][c].multicolumn != Tabular::CELL_PART_OF_MULTICOLUMN)
969                         ++result;
970         return result;
971 }
972
973
974 bool Tabular::topLine(idx_type const cell) const
975 {
976         return cellInfo(cell).top_line;
977 }
978
979
980 bool Tabular::bottomLine(idx_type const cell) const
981 {
982         return cellInfo(cell).bottom_line;
983 }
984
985
986 bool Tabular::leftLine(idx_type cell, bool const ignore_bt) const
987 {
988         if (use_booktabs && !ignore_bt)
989                 return false;
990         return cellInfo(cell).left_line;
991 }
992
993
994 bool Tabular::rightLine(idx_type cell, bool const ignore_bt) const
995 {
996         if (use_booktabs && !ignore_bt)
997                 return false;
998         return cellInfo(cell).right_line;
999 }
1000
1001
1002 pair<bool, bool> Tabular::topLineTrim(idx_type const cell) const
1003 {
1004         if (!use_booktabs)
1005                 return make_pair(false, false);
1006         return make_pair(cellInfo(cell).top_line_ltrimmed,
1007                          cellInfo(cell).top_line_rtrimmed);
1008 }
1009
1010
1011 pair<bool, bool> Tabular::bottomLineTrim(idx_type const cell) const
1012 {
1013         if (!use_booktabs)
1014                 return make_pair(false, false);
1015         return make_pair(cellInfo(cell).bottom_line_ltrimmed,
1016                          cellInfo(cell).bottom_line_rtrimmed);
1017 }
1018
1019
1020 int Tabular::interRowSpace(row_type row) const
1021 {
1022         if (!row || row >= nrows())
1023                 return 0;
1024
1025         int const interline_space = row_info[row - 1].interline_space_default ?
1026                 default_line_space :
1027                 row_info[row - 1].interline_space.inPixels(width());
1028         if (rowTopLine(row) && rowBottomLine(row - 1))
1029                 return interline_space + WIDTH_OF_LINE;
1030         return interline_space;
1031 }
1032
1033
1034 int Tabular::interColumnSpace(idx_type cell) const
1035 {
1036         col_type const nextcol = cellColumn(cell) + columnSpan(cell);
1037         if (rightLine(cell) && nextcol < ncols()
1038                 && leftLine(cellIndex(cellRow(cell), nextcol)))
1039                 return WIDTH_OF_LINE;
1040         return 0;
1041 }
1042
1043
1044 int Tabular::cellWidth(idx_type cell) const
1045 {
1046         int w = 0;
1047         col_type const span = columnSpan(cell);
1048         col_type const col = cellColumn(cell);
1049         for(col_type c = col; c < col + span ; ++c)
1050                 w += column_info[c].width;
1051         return w;
1052 }
1053
1054
1055 int Tabular::cellHeight(idx_type cell) const
1056 {
1057         row_type const span = rowSpan(cell);
1058         row_type const row = cellRow(cell);
1059         int h = 0;
1060         for(row_type r = row; r < row + span ; ++r) {
1061                 h += rowAscent(r) + rowDescent(r);
1062                 if (r != row + span - 1)
1063                         h += interRowSpace(r + 1);
1064         }
1065
1066         return h;
1067 }
1068
1069
1070 bool Tabular::updateColumnWidths(MetricsInfo & mi)
1071 {
1072         vector<int> max_dwidth(ncols(), 0);
1073         // collect max. fixed width of column
1074         map<col_type, int> max_pwidth;
1075         // collect max. variable width of column
1076         map<col_type, int> max_width;
1077
1078         for(col_type c = 0; c < ncols(); ++c)
1079                 for(row_type r = 0; r < nrows(); ++r) {
1080                         idx_type const i = cellIndex(r, c);
1081                         if (getAlignment(i) == LYX_ALIGN_DECIMAL)
1082                                 max_dwidth[c] = max(max_dwidth[c], cell_info[r][c].decimal_width);
1083                         if (!getPWidth(i).zero())
1084                                 max_pwidth[c] = max(max_pwidth[c], cell_info[r][c].width);
1085                         else if (!column_info[c].varwidth)
1086                                 max_width[c] = max(max_width[c], cell_info[r][c].width);
1087                 }
1088
1089         // If we have a fixed tabular width, we take this into account
1090         Length tab_width = tabular_width;
1091         bool const tabularx = hasVarwidthColumn();
1092         if (tabularx && tab_width.zero())
1093                 // If no tabular width is specified with X columns,
1094                 // we use 100% colwidth
1095                 tab_width = Length(100, Length::PCW);
1096         int restwidth = -1;
1097         if (!tab_width.zero()) {
1098                 restwidth = mi.base.inPixels(tab_width);
1099                 // Substract the fixed widths from the table width
1100                 for (auto const w : max_pwidth)
1101                         restwidth -= w.second;
1102         }
1103
1104         // If we have a fixed width, distribute the available table width
1105         // (minus the fixed widths) to the variable-width columns
1106         int vcolwidth = -1;
1107         int restcols = ncols() - max_pwidth.size();
1108         if ((restwidth > 0) && (restcols != 0))
1109                 vcolwidth = restwidth / restcols;
1110
1111         // Now consider that some variable width columns exceed the vcolwidth
1112         if (vcolwidth > 0) {
1113                 bool changed = false;
1114                 for (auto const w : max_width) {
1115                         if (tabularx || w.second > vcolwidth) {
1116                                 --restcols;
1117                                 restwidth -= w.second;
1118                                 changed = true;
1119                         }
1120                 }
1121                 if (changed && restwidth > 0)
1122                         vcolwidth = restwidth / restcols;
1123         }
1124
1125         bool update = false;
1126         // for each col get max of single col cells
1127         for(col_type c = 0; c < ncols(); ++c) {
1128                 int new_width = 0;
1129                 for(row_type r = 0; r < nrows(); ++r) {
1130                         idx_type const i = cellIndex(r, c);
1131                         if (columnSpan(i) == 1) {
1132                                 if (getAlignment(i) == LYX_ALIGN_DECIMAL
1133                                         && cell_info[r][c].decimal_width != 0)
1134                                         new_width = max(new_width, cellInfo(i).width
1135                                                 + max_dwidth[c] - cellInfo(i).decimal_width);
1136                                 else if (getPWidth(i).zero() && vcolwidth > 0) {
1137                                         if (tabularx && !column_info[c].varwidth)
1138                                                 new_width = max(new_width, cellInfo(i).width);
1139                                         else if (tabularx)
1140                                                 new_width = vcolwidth;
1141                                         else
1142                                                 new_width = max(vcolwidth, max(new_width, cellInfo(i).width));
1143                                 } else
1144                                         new_width = max(new_width, cellInfo(i).width);
1145                         }
1146                 }
1147
1148                 if (column_info[c].width != new_width) {
1149                         column_info[c].width = new_width;
1150                         // Do not trigger update when no space is left for variable
1151                         // columns, as this will loop
1152                         update = tab_width.zero() || restwidth > 0;
1153                 }
1154         }
1155         // update col widths to fit merged cells
1156         for(col_type c = 0; c < ncols(); ++c)
1157                 for(row_type r = 0; r < nrows(); ++r) {
1158                         idx_type const i = cellIndex(r, c);
1159                         int const span = columnSpan(i);
1160                         if (span == 1 || c > cellColumn(i))
1161                                 continue;
1162
1163                         int old_width = 0;
1164                         for(col_type j = c; j < c + span ; ++j)
1165                                 old_width += column_info[j].width;
1166
1167                         if (cellInfo(i).width > old_width) {
1168                                 column_info[c + span - 1].width += cellInfo(i).width - old_width;
1169                                 // Do not trigger update when no space is left for variable
1170                                 // columns, as this will loop
1171                                 update = tab_width.zero() || restwidth > 0;
1172                         }
1173                 }
1174
1175         return update;
1176 }
1177
1178
1179 int Tabular::width() const
1180 {
1181         int width = 0;
1182         for (col_type c = 0; c < ncols(); ++c)
1183                 width += column_info[c].width;
1184         return width;
1185 }
1186
1187
1188 void Tabular::setAlignment(idx_type cell, LyXAlignment align,
1189                               bool has_width)
1190 {
1191         col_type const col = cellColumn(cell);
1192         // set alignment for the whole row if we are not in a multicolumn cell,
1193         // exclude possible multicolumn cells in the row
1194         if (!isMultiColumn(cell)) {
1195                 for (row_type r = 0; r < nrows(); ++r) {
1196                         // only if the column has no width the multirow inherits the
1197                         // alignment of the column, otherwise it is left aligned
1198                         if (!(isMultiRow(cellIndex(r, col)) && has_width)
1199                                 && !isMultiColumn(cellIndex(r, col))) {
1200                                 cell_info[r][col].alignment = align;
1201                                 cell_info[r][col].inset->setContentAlignment(align);
1202                         }
1203                         if ((isMultiRow(cellIndex(r, col)) && has_width)
1204                                 && !isMultiColumn(cellIndex(r, col))) {
1205                                 cell_info[r][col].alignment = LYX_ALIGN_LEFT;
1206                                 cell_info[r][col].inset->setContentAlignment(LYX_ALIGN_LEFT);
1207                         }
1208                 }
1209                 column_info[col].alignment = align;
1210                 docstring & dpoint = column_info[col].decimal_point;
1211                 if (align == LYX_ALIGN_DECIMAL && dpoint.empty()) {
1212                         Language const * tlang = buffer().paragraphs().front().getParLanguage(buffer().params());
1213                         dpoint = tlang->decimalSeparator();
1214                 }
1215         } else {
1216                 cellInfo(cell).alignment = align;
1217                 cellInset(cell)->setContentAlignment(align);
1218         }
1219 }
1220
1221
1222 void Tabular::setVAlignment(idx_type cell, VAlignment align,
1223                                bool onlycolumn)
1224 {
1225         if (!isMultiColumn(cell) || onlycolumn)
1226                 column_info[cellColumn(cell)].valignment = align;
1227         if (!onlycolumn)
1228                 cellInfo(cell).valignment = align;
1229 }
1230
1231
1232 namespace {
1233
1234 /**
1235  * Allow line and paragraph breaks for fixed width multicol/multirow cells
1236  * or disallow them, merge cell paragraphs and reset layout to standard
1237  * for variable width multicol cells.
1238  */
1239 void toggleFixedWidth(Cursor & cur, InsetTableCell * inset,
1240                       bool const fixedWidth, bool const multicol,
1241                       bool const multirow)
1242 {
1243         inset->toggleFixedWidth(fixedWidth);
1244         if (!multirow && (fixedWidth || !multicol))
1245                 return;
1246
1247         // merge all paragraphs to one
1248         BufferParams const & bp = cur.bv().buffer().params();
1249         while (inset->paragraphs().size() > 1)
1250                 mergeParagraph(bp, inset->paragraphs(), 0);
1251
1252         // This is relevant for multirows
1253         if (fixedWidth)
1254                 return;
1255
1256         // remove newlines
1257         ParagraphList::iterator pit = inset->paragraphs().begin();
1258         for (; pit != inset->paragraphs().end(); ++pit) {
1259                 for (pos_type j = 0; j != pit->size(); ++j) {
1260                         if (pit->isNewline(j))
1261                                 pit->eraseChar(j, bp.track_changes);
1262                 }
1263         }
1264
1265         // reset layout
1266         cur.push(*inset);
1267         // undo information has already been recorded
1268         inset->getText(0)->setLayout(0, cur.lastpit() + 1,
1269                         bp.documentClass().plainLayoutName());
1270         cur.pop();
1271 }
1272
1273 } // namespace
1274
1275
1276 void Tabular::setColumnPWidth(Cursor & cur, idx_type cell,
1277                 Length const & width)
1278 {
1279         col_type const c = cellColumn(cell);
1280
1281         column_info[c].p_width = width;
1282         // reset the vertical alignment to top if the fixed width
1283         // is removed or zero because only fixed width columns can
1284         // have a vertical alignment
1285         if (column_info[c].p_width.zero())
1286                 column_info[c].valignment = LYX_VALIGN_TOP;
1287         for (row_type r = 0; r < nrows(); ++r) {
1288                 idx_type const cidx = cellIndex(r, c);
1289                 // because of multicolumns
1290                 toggleFixedWidth(cur, cellInset(cidx).get(),
1291                                  !getPWidth(cidx).zero(), isMultiColumn(cidx),
1292                                  isMultiRow(cidx));
1293                 if (isMultiRow(cidx))
1294                         setAlignment(cidx, LYX_ALIGN_LEFT, false);
1295         }
1296         // cur can become invalid after paragraphs were merged
1297         cur.fixIfBroken();
1298 }
1299
1300
1301 bool Tabular::setFixedWidth(row_type r, col_type c)
1302 {
1303         bool const multicol = cell_info[r][c].multicolumn != CELL_NORMAL;
1304         bool const fixed_width = (!column_info[c].p_width.zero() && !multicol)
1305               || (multicol && !cell_info[r][c].p_width.zero());
1306         cell_info[r][c].inset->toggleFixedWidth(fixed_width);
1307         return fixed_width;
1308 }
1309
1310
1311 bool Tabular::setMColumnPWidth(Cursor & cur, idx_type cell,
1312                 Length const & width)
1313 {
1314         if (!isMultiColumn(cell))
1315                 return false;
1316
1317         cellInfo(cell).p_width = width;
1318         toggleFixedWidth(cur, cellInset(cell).get(), !width.zero(),
1319                          isMultiColumn(cell), isMultiRow(cell));
1320         // cur can become invalid after paragraphs were merged
1321         cur.fixIfBroken();
1322         return true;
1323 }
1324
1325
1326 bool Tabular::toggleVarwidth(idx_type cell, bool const varwidth)
1327 {
1328         column_info[cellColumn(cell)].varwidth = varwidth;
1329         return true;
1330 }
1331
1332
1333 bool Tabular::setMROffset(Cursor &, idx_type cell, Length const & mroffset)
1334 {
1335         cellInfo(cell).mroffset = mroffset;
1336         return true;
1337 }
1338
1339
1340 void Tabular::setAlignSpecial(idx_type cell, docstring const & special,
1341                                  Tabular::Feature what)
1342 {
1343         if (what == SET_SPECIAL_MULTICOLUMN)
1344                 cellInfo(cell).align_special = special;
1345         else
1346                 column_info[cellColumn(cell)].align_special = special;
1347 }
1348
1349
1350 void Tabular::setTopLine(idx_type i, bool line)
1351 {
1352         cellInfo(i).top_line = line;
1353 }
1354
1355
1356 void Tabular::setBottomLine(idx_type i, bool line)
1357 {
1358         cellInfo(i).bottom_line = line;
1359 }
1360
1361
1362 void Tabular::setTopLineLTrim(idx_type i, bool val)
1363 {
1364         cellInfo(i).top_line_ltrimmed = val;
1365 }
1366
1367
1368 void Tabular::setTopLineRTrim(idx_type i, bool val)
1369 {
1370         cellInfo(i).top_line_rtrimmed = val;
1371 }
1372
1373
1374 void Tabular::setBottomLineLTrim(idx_type i, bool val)
1375 {
1376         cellInfo(i).bottom_line_ltrimmed = val;
1377 }
1378
1379
1380 void Tabular::setBottomLineRTrim(idx_type i, bool val)
1381 {
1382         cellInfo(i).bottom_line_rtrimmed = val;
1383 }
1384
1385
1386 void Tabular::setTopLineTrim(idx_type i, pair<bool, bool> trim)
1387 {
1388         setTopLineLTrim(i, trim.first);
1389         setTopLineRTrim(i, trim.second);
1390 }
1391
1392 void Tabular::setBottomLineTrim(idx_type i, pair<bool, bool> trim)
1393 {
1394         setBottomLineLTrim(i, trim.first);
1395         setBottomLineRTrim(i, trim.second);
1396 }
1397
1398 void Tabular::setLeftLine(idx_type cell, bool line)
1399 {
1400         cellInfo(cell).left_line = line;
1401 }
1402
1403
1404 void Tabular::setRightLine(idx_type cell, bool line)
1405 {
1406         cellInfo(cell).right_line = line;
1407 }
1408
1409 bool Tabular::rowTopLine(row_type r) const
1410 {
1411         bool all_rows_set = true;
1412         for (col_type c = 0; all_rows_set && c < ncols(); ++c)
1413                 all_rows_set = cellInfo(cellIndex(r, c)).top_line;
1414         return all_rows_set;
1415 }
1416
1417
1418 bool Tabular::rowBottomLine(row_type r) const
1419 {
1420         bool all_rows_set = true;
1421         for (col_type c = 0; all_rows_set && c < ncols(); ++c)
1422                 all_rows_set = cellInfo(cellIndex(r, c)).bottom_line;
1423         return all_rows_set;
1424 }
1425
1426
1427 bool Tabular::columnLeftLine(col_type c) const
1428 {
1429         if (use_booktabs)
1430                 return false;
1431
1432         int nrows_left = 0;
1433         int total = 0;
1434         for (row_type r = 0; r < nrows(); ++r) {
1435                 idx_type const i = cellIndex(r, c);
1436                 if (c == cellColumn(i)) {
1437                         ++total;
1438                         bool right = c > 0 && cellInfo(cellIndex(r, c - 1)).right_line;
1439                         if (cellInfo(i).left_line || right)
1440                                 ++nrows_left;
1441                 }
1442         }
1443         return 2 * nrows_left >= total;
1444 }
1445
1446
1447 bool Tabular::columnRightLine(col_type c) const
1448 {
1449         if (use_booktabs)
1450                 return false;
1451
1452         int nrows_right = 0;
1453         int total = 0;
1454         for (row_type r = 0; r < nrows(); ++r) {
1455                 idx_type i = cellIndex(r, c);
1456                 if (c == cellColumn(i) + columnSpan(i) - 1) {
1457                         ++total;
1458                         bool left = (c + 1 < ncols()
1459                                 && cellInfo(cellIndex(r, c + 1)).left_line)
1460                                 || c + 1 == ncols();
1461                         if (cellInfo(i).right_line && left)
1462                                 ++nrows_right;
1463                 }
1464         }
1465         return 2 * nrows_right >= total;
1466 }
1467
1468
1469 LyXAlignment Tabular::getAlignment(idx_type cell, bool onlycolumn) const
1470 {
1471         if (!onlycolumn && (isMultiColumn(cell) || isMultiRow(cell)))
1472                 return cellInfo(cell).alignment;
1473
1474         return column_info[cellColumn(cell)].alignment;
1475 }
1476
1477
1478 Tabular::VAlignment
1479 Tabular::getVAlignment(idx_type cell, bool onlycolumn) const
1480 {
1481         if (!onlycolumn && (isMultiColumn(cell) || isMultiRow(cell)))
1482                 return cellInfo(cell).valignment;
1483         return column_info[cellColumn(cell)].valignment;
1484 }
1485
1486
1487 int Tabular::offsetVAlignment() const
1488 {
1489         // for top-alignment the first horizontal table line must be exactly at
1490         // the position of the base line of the surrounding text line
1491         // for bottom alignment, the same is for the last table line
1492         int offset_valign = 0;
1493         switch (tabular_valignment) {
1494         case Tabular::LYX_VALIGN_BOTTOM:
1495                 offset_valign = rowAscent(0) - height();
1496                 break;
1497         case Tabular::LYX_VALIGN_MIDDLE:
1498                 offset_valign = (- height()) / 2 + rowAscent(0);
1499                 break;
1500         case Tabular::LYX_VALIGN_TOP:
1501                 offset_valign = rowAscent(0);
1502                 break;
1503         }
1504         return offset_valign;
1505 }
1506
1507
1508 Length const Tabular::getPWidth(idx_type cell) const
1509 {
1510         if (isMultiColumn(cell))
1511                 return cellInfo(cell).p_width;
1512         return column_info[cellColumn(cell)].p_width;
1513 }
1514
1515
1516 Length const Tabular::getMROffset(idx_type cell) const
1517 {
1518         return cellInfo(cell).mroffset;
1519 }
1520
1521
1522 int Tabular::textHOffset(idx_type cell) const
1523 {
1524         // the LaTeX Way :-(
1525         int x = WIDTH_OF_LINE;
1526
1527         int const w = cellWidth(cell) - cellInfo(cell).width;
1528
1529         switch (getAlignment(cell)) {
1530         case LYX_ALIGN_CENTER:
1531                 x += w / 2;
1532                 break;
1533         case LYX_ALIGN_RIGHT:
1534                 x += w;
1535                 break;
1536         case LYX_ALIGN_DECIMAL: {
1537                 // we center when no decimal point
1538                 if (cellInfo(cell).decimal_width == 0) {
1539                         x += w / 2;
1540                         break;
1541                 }
1542                 col_type const c = cellColumn(cell);
1543                 int max_dhoffset = 0;
1544                 for(row_type r = 0; r < row_info.size() ; ++r) {
1545                         idx_type const i = cellIndex(r, c);
1546                         if (getAlignment(i) == LYX_ALIGN_DECIMAL
1547                                 && cellInfo(i).decimal_width != 0)
1548                                 max_dhoffset = max(max_dhoffset, cellInfo(i).decimal_hoffset);
1549                 }
1550                 x += max_dhoffset - cellInfo(cell).decimal_hoffset;
1551         }
1552         default:
1553                 // LYX_ALIGN_LEFT: nothing :-)
1554                 break;
1555         }
1556
1557         return x;
1558 }
1559
1560
1561 int Tabular::textVOffset(idx_type cell) const
1562 {
1563         int voffset = cellInfo(cell).voffset;
1564         if (isMultiRow(cell)) {
1565                 row_type const row = cellRow(cell);
1566                 voffset += (cellHeight(cell) - rowAscent(row) - rowDescent(row))/2;
1567         }
1568         return voffset;
1569 }
1570
1571
1572 Tabular::idx_type Tabular::getFirstCellInRow(row_type row) const
1573 {
1574         col_type c = 0;
1575         idx_type const numcells = numberOfCellsInRow(row);
1576         // we check against numcells to make sure we do not crash if all the
1577         // cells are multirow (bug #7535), but in that case our return value
1578         // is really invalid, i.e., it is NOT the first cell in the row. but
1579         // i do not know what to do here. (rgh)
1580         while (c < numcells - 1
1581                && cell_info[row][c].multirow == CELL_PART_OF_MULTIROW)
1582                 ++c;
1583         return cell_info[row][c].cellno;
1584 }
1585
1586
1587 Tabular::idx_type Tabular::getLastCellInRow(row_type row) const
1588 {
1589         col_type c = ncols() - 1;
1590         // of course we check against 0 so we don't crash. but we have the same
1591         // problem as in the previous routine: if all the cells are part of a
1592         // multirow or part of a multi column, then our return value is invalid.
1593         while (c > 0
1594                && (cell_info[row][c].multirow == CELL_PART_OF_MULTIROW
1595                    || cell_info[row][c].multicolumn == CELL_PART_OF_MULTICOLUMN))
1596                 --c;
1597         return cell_info[row][c].cellno;
1598 }
1599
1600
1601 Tabular::row_type Tabular::cellRow(idx_type cell) const
1602 {
1603         if (cell >= numberofcells)
1604                 return nrows() - 1;
1605         if (cell == npos)
1606                 return 0;
1607         return rowofcell[cell];
1608 }
1609
1610
1611 Tabular::col_type Tabular::cellColumn(idx_type cell) const
1612 {
1613         if (cell >= numberofcells)
1614                 return ncols() - 1;
1615         if (cell == npos)
1616                 return 0;
1617         return columnofcell[cell];
1618 }
1619
1620
1621 void Tabular::write(ostream & os) const
1622 {
1623         // header line
1624         os << "<lyxtabular"
1625            << write_attribute("version", 3)
1626            << write_attribute("rows", nrows())
1627            << write_attribute("columns", ncols())
1628            << ">\n";
1629         // global longtable options
1630         os << "<features"
1631            << write_attribute("rotate", rotate)
1632            << write_attribute("booktabs", use_booktabs)
1633            << write_attribute("islongtable", is_long_tabular)
1634            << write_attribute("firstHeadTopDL", endfirsthead.topDL)
1635            << write_attribute("firstHeadBottomDL", endfirsthead.bottomDL)
1636            << write_attribute("firstHeadEmpty", endfirsthead.empty)
1637            << write_attribute("headTopDL", endhead.topDL)
1638            << write_attribute("headBottomDL", endhead.bottomDL)
1639            << write_attribute("footTopDL", endfoot.topDL)
1640            << write_attribute("footBottomDL", endfoot.bottomDL)
1641            << write_attribute("lastFootTopDL", endlastfoot.topDL)
1642            << write_attribute("lastFootBottomDL", endlastfoot.bottomDL)
1643            << write_attribute("lastFootEmpty", endlastfoot.empty);
1644         // longtables cannot be aligned vertically
1645         if (!is_long_tabular)
1646                 os << write_attribute("tabularvalignment", tabular_valignment);
1647         os << write_attribute("tabularwidth", tabular_width);
1648         if (is_long_tabular)
1649                 os << write_attribute("longtabularalignment", longtabular_alignment);
1650         os << ">\n";
1651         for (col_type c = 0; c < ncols(); ++c) {
1652                 os << "<column"
1653                    << write_attribute("alignment", column_info[c].alignment);
1654                 if (column_info[c].alignment == LYX_ALIGN_DECIMAL)
1655                    os << write_attribute("decimal_point", column_info[c].decimal_point);
1656                 os << write_attribute("valignment", column_info[c].valignment)
1657                    << write_attribute("width", column_info[c].p_width.asString())
1658                    << write_attribute("varwidth", column_info[c].varwidth)
1659                    << write_attribute("special", column_info[c].align_special)
1660                    << ">\n";
1661         }
1662         for (row_type r = 0; r < nrows(); ++r) {
1663                 static const string def("default");
1664                 os << "<row";
1665                 if (row_info[r].top_space_default)
1666                         os << write_attribute("topspace", def);
1667                 else
1668                         os << write_attribute("topspace", row_info[r].top_space);
1669                 if (row_info[r].bottom_space_default)
1670                         os << write_attribute("bottomspace", def);
1671                 else
1672                         os << write_attribute("bottomspace", row_info[r].bottom_space);
1673                 if (row_info[r].interline_space_default)
1674                         os << write_attribute("interlinespace", def);
1675                 else
1676                         os << write_attribute("interlinespace", row_info[r].interline_space);
1677                 os << write_attribute("endhead", row_info[r].endhead)
1678                    << write_attribute("endfirsthead", row_info[r].endfirsthead)
1679                    << write_attribute("endfoot", row_info[r].endfoot)
1680                    << write_attribute("endlastfoot", row_info[r].endlastfoot)
1681                    << write_attribute("newpage", row_info[r].newpage)
1682                    << write_attribute("caption", row_info[r].caption)
1683                    << ">\n";
1684                 for (col_type c = 0; c < ncols(); ++c) {
1685                         os << "<cell"
1686                            << write_attribute("multicolumn", cell_info[r][c].multicolumn)
1687                            << write_attribute("multirow", cell_info[r][c].multirow)
1688                            << write_attribute("mroffset", cell_info[r][c].mroffset)
1689                            << write_attribute("alignment", cell_info[r][c].alignment)
1690                            << write_attribute("valignment", cell_info[r][c].valignment)
1691                            << write_attribute("topline", cell_info[r][c].top_line)
1692                            << write_attribute("toplineltrim", cell_info[r][c].top_line_ltrimmed)
1693                            << write_attribute("toplinertrim", cell_info[r][c].top_line_rtrimmed)
1694                            << write_attribute("bottomline", cell_info[r][c].bottom_line)
1695                            << write_attribute("bottomlineltrim", cell_info[r][c].bottom_line_ltrimmed)
1696                            << write_attribute("bottomlinertrim", cell_info[r][c].bottom_line_rtrimmed)
1697                            << write_attribute("leftline", cell_info[r][c].left_line)
1698                            << write_attribute("rightline", cell_info[r][c].right_line)
1699                            << write_attribute("rotate", cell_info[r][c].rotate)
1700                            << write_attribute("usebox", cell_info[r][c].usebox)
1701                            << write_attribute("width", cell_info[r][c].p_width)
1702                            << write_attribute("special", cell_info[r][c].align_special)
1703                            << ">\n";
1704                         os << "\\begin_inset ";
1705                         cell_info[r][c].inset->write(os);
1706                         os << "\n\\end_inset\n"
1707                            << "</cell>\n";
1708                         // FIXME This can be removed again once the mystery
1709                         // crash has been resolved.
1710                         os << flush;
1711                 }
1712                 os << "</row>\n";
1713         }
1714         os << "</lyxtabular>\n";
1715 }
1716
1717
1718 void Tabular::read(Lexer & lex)
1719 {
1720         string line;
1721         istream & is = lex.getStream();
1722
1723         l_getline(is, line);
1724         if (!prefixIs(line, "<lyxtabular ") && !prefixIs(line, "<Tabular ")) {
1725                 LASSERT(false, return);
1726         }
1727
1728         int version;
1729         if (!getTokenValue(line, "version", version))
1730                 return;
1731         LATTEST(version >= 2);
1732
1733         int rows_arg;
1734         if (!getTokenValue(line, "rows", rows_arg))
1735                 return;
1736         int columns_arg;
1737         if (!getTokenValue(line, "columns", columns_arg))
1738                 return;
1739         init(buffer_, rows_arg, columns_arg);
1740         l_getline(is, line);
1741         if (!prefixIs(line, "<features")) {
1742                 lyxerr << "Wrong tabular format (expected <features ...> got"
1743                        << line << ')' << endl;
1744                 return;
1745         }
1746         getTokenValue(line, "rotate", rotate);
1747         getTokenValue(line, "booktabs", use_booktabs);
1748         getTokenValue(line, "islongtable", is_long_tabular);
1749         getTokenValue(line, "tabularvalignment", tabular_valignment);
1750         getTokenValue(line, "tabularwidth", tabular_width);
1751         getTokenValue(line, "longtabularalignment", longtabular_alignment);
1752         getTokenValue(line, "firstHeadTopDL", endfirsthead.topDL);
1753         getTokenValue(line, "firstHeadBottomDL", endfirsthead.bottomDL);
1754         getTokenValue(line, "firstHeadEmpty", endfirsthead.empty);
1755         getTokenValue(line, "headTopDL", endhead.topDL);
1756         getTokenValue(line, "headBottomDL", endhead.bottomDL);
1757         getTokenValue(line, "footTopDL", endfoot.topDL);
1758         getTokenValue(line, "footBottomDL", endfoot.bottomDL);
1759         getTokenValue(line, "lastFootTopDL", endlastfoot.topDL);
1760         getTokenValue(line, "lastFootBottomDL", endlastfoot.bottomDL);
1761         getTokenValue(line, "lastFootEmpty", endlastfoot.empty);
1762
1763         for (col_type c = 0; c < ncols(); ++c) {
1764                 l_getline(is,line);
1765                 if (!prefixIs(line,"<column")) {
1766                         lyxerr << "Wrong tabular format (expected <column ...> got"
1767                                << line << ')' << endl;
1768                         return;
1769                 }
1770                 getTokenValue(line, "alignment", column_info[c].alignment);
1771                 getTokenValue(line, "decimal_point", column_info[c].decimal_point);
1772                 getTokenValue(line, "valignment", column_info[c].valignment);
1773                 getTokenValue(line, "width", column_info[c].p_width);
1774                 getTokenValue(line, "special", column_info[c].align_special);
1775                 getTokenValue(line, "varwidth", column_info[c].varwidth);
1776         }
1777
1778         for (row_type i = 0; i < nrows(); ++i) {
1779                 l_getline(is, line);
1780                 if (!prefixIs(line, "<row")) {
1781                         lyxerr << "Wrong tabular format (expected <row ...> got"
1782                                << line << ')' << endl;
1783                         return;
1784                 }
1785                 getTokenValue(line, "topspace", row_info[i].top_space,
1786                               row_info[i].top_space_default);
1787                 getTokenValue(line, "bottomspace", row_info[i].bottom_space,
1788                               row_info[i].bottom_space_default);
1789                 getTokenValue(line, "interlinespace", row_info[i].interline_space,
1790                               row_info[i].interline_space_default);
1791                 getTokenValue(line, "endfirsthead", row_info[i].endfirsthead);
1792                 getTokenValue(line, "endhead", row_info[i].endhead);
1793                 getTokenValue(line, "endfoot", row_info[i].endfoot);
1794                 getTokenValue(line, "endlastfoot", row_info[i].endlastfoot);
1795                 getTokenValue(line, "newpage", row_info[i].newpage);
1796                 getTokenValue(line, "caption", row_info[i].caption);
1797                 for (col_type j = 0; j < ncols(); ++j) {
1798                         l_getline(is, line);
1799                         if (!prefixIs(line, "<cell")) {
1800                                 lyxerr << "Wrong tabular format (expected <cell ...> got"
1801                                        << line << ')' << endl;
1802                                 return;
1803                         }
1804                         getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn);
1805                         getTokenValue(line, "multirow", cell_info[i][j].multirow);
1806                         getTokenValue(line, "mroffset", cell_info[i][j].mroffset);
1807                         getTokenValue(line, "alignment", cell_info[i][j].alignment);
1808                         getTokenValue(line, "valignment", cell_info[i][j].valignment);
1809                         getTokenValue(line, "topline", cell_info[i][j].top_line);
1810                         getTokenValue(line, "toplineltrim", cell_info[i][j].top_line_ltrimmed);
1811                         getTokenValue(line, "toplinertrim", cell_info[i][j].top_line_rtrimmed);
1812                         getTokenValue(line, "bottomline", cell_info[i][j].bottom_line);
1813                         getTokenValue(line, "bottomlineltrim", cell_info[i][j].bottom_line_ltrimmed);
1814                         getTokenValue(line, "bottomlinertrim", cell_info[i][j].bottom_line_rtrimmed);
1815                         getTokenValue(line, "leftline", cell_info[i][j].left_line);
1816                         getTokenValue(line, "rightline", cell_info[i][j].right_line);
1817                         getTokenValue(line, "rotate", cell_info[i][j].rotate);
1818                         getTokenValue(line, "usebox", cell_info[i][j].usebox);
1819                         getTokenValue(line, "width", cell_info[i][j].p_width);
1820                         setFixedWidth(i,j);
1821                         getTokenValue(line, "special", cell_info[i][j].align_special);
1822                         l_getline(is, line);
1823                         if (prefixIs(line, "\\begin_inset")) {
1824                                 cell_info[i][j].inset->setBuffer(*buffer_);
1825                                 cell_info[i][j].inset->read(lex);
1826                                 l_getline(is, line);
1827                         }
1828                         if (!prefixIs(line, "</cell>")) {
1829                                 lyxerr << "Wrong tabular format (expected </cell> got"
1830                                        << line << ')' << endl;
1831                                 return;
1832                         }
1833                 }
1834                 l_getline(is, line);
1835                 if (!prefixIs(line, "</row>")) {
1836                         lyxerr << "Wrong tabular format (expected </row> got"
1837                                << line << ')' << endl;
1838                         return;
1839                 }
1840         }
1841         while (!prefixIs(line, "</lyxtabular>")) {
1842                 l_getline(is, line);
1843         }
1844         updateIndexes();
1845 }
1846
1847
1848 bool Tabular::isMultiColumn(idx_type cell) const
1849 {
1850         return (cellInfo(cell).multicolumn == CELL_BEGIN_OF_MULTICOLUMN
1851                 || cellInfo(cell).multicolumn == CELL_PART_OF_MULTICOLUMN);
1852 }
1853
1854
1855 bool Tabular::hasMultiColumn(col_type c) const
1856 {
1857         for (row_type r = 0; r < nrows(); ++r) {
1858                 if (isMultiColumn(cellIndex(r, c)))
1859                         return true;
1860         }
1861         return false;
1862 }
1863
1864
1865 bool Tabular::hasVarwidthColumn() const
1866 {
1867         for (col_type c = 0; c < ncols(); ++c) {
1868                 if (column_info[c].varwidth)
1869                         return true;
1870         }
1871         return false;
1872 }
1873
1874
1875 bool Tabular::isVTypeColumn(col_type c) const
1876 {
1877         for (row_type r = 0; r < nrows(); ++r) {
1878                 idx_type idx = cellIndex(r, c);
1879                 if (getRotateCell(idx) == 0 && useBox(idx) == BOX_VARWIDTH)
1880                         return true;
1881         }
1882         return false;
1883 }
1884
1885
1886 Tabular::CellData const & Tabular::cellInfo(idx_type cell) const
1887 {
1888         return cell_info[cellRow(cell)][cellColumn(cell)];
1889 }
1890
1891
1892 Tabular::CellData & Tabular::cellInfo(idx_type cell)
1893 {
1894         return cell_info[cellRow(cell)][cellColumn(cell)];
1895 }
1896
1897
1898 Tabular::idx_type Tabular::setMultiColumn(Cursor & cur, idx_type cell, idx_type number,
1899                                           bool const right_border)
1900 {
1901         idx_type const col = cellColumn(cell);
1902         idx_type const row = cellRow(cell);
1903         for (idx_type i = 0; i < number; ++i)
1904                 unsetMultiRow(cellIndex(row, col + i));
1905
1906         // unsetting of multirow may have invalidated cell index
1907         cell = cellIndex(row, col);
1908         CellData & cs = cellInfo(cell);
1909         cs.multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
1910         if (column_info[col].alignment != LYX_ALIGN_DECIMAL)
1911                 cs.alignment = column_info[col].alignment;
1912         setRightLine(cell, right_border);
1913         // non-fixed width multicolumns cannot have multiple paragraphs
1914         if (getPWidth(cell).zero()) {
1915                 toggleFixedWidth(cur, cellInset(cell).get(),
1916                                  !getPWidth(cell).zero(), isMultiColumn(cell),
1917                                  isMultiRow(cell));
1918                 // cur can become invalid after paragraphs were merged
1919                 cur.fixIfBroken();
1920         }
1921
1922         idx_type lastcell = cellIndex(row, col + number - 1);
1923         for (idx_type i = 1; i < lastcell - cell + 1; ++i) {
1924                 CellData & cs1 = cellInfo(cell + i);
1925                 cs1.multicolumn = CELL_PART_OF_MULTICOLUMN;
1926                 cs.inset->appendParagraphs(cs1.inset->paragraphs());
1927                 cs1.inset->clear();
1928         }
1929         updateIndexes();
1930         return cell;
1931 }
1932
1933
1934 bool Tabular::isMultiRow(idx_type cell) const
1935 {
1936         return (cellInfo(cell).multirow == CELL_BEGIN_OF_MULTIROW
1937                 || cellInfo(cell).multirow == CELL_PART_OF_MULTIROW);
1938 }
1939
1940 bool Tabular::hasMultiRow(row_type r) const
1941 {
1942         for (col_type c = 0; c < ncols(); ++c) {
1943                 if (isMultiRow(cellIndex(r, c)))
1944                         return true;
1945         }
1946         return false;
1947 }
1948
1949 Tabular::idx_type Tabular::setMultiRow(Cursor & cur, idx_type cell, idx_type number,
1950                                        bool const bottom_border,
1951                                        LyXAlignment const halign)
1952 {
1953         idx_type const col = cellColumn(cell);
1954         idx_type const row = cellRow(cell);
1955         for (idx_type i = 0; i < number; ++i)
1956                 unsetMultiColumn(cellIndex(row + i, col));
1957
1958         // unsetting of multirow may have invalidated cell index
1959         cell = cellIndex(row, col);
1960         CellData & cs = cellInfo(cell);
1961         cs.multirow = CELL_BEGIN_OF_MULTIROW;
1962         cs.valignment = LYX_VALIGN_MIDDLE;
1963         // the horizontal alignment of multirow cells can only
1964         // be changed for the whole table row,
1965         // support changing this only for the multirow cell can be done via
1966         // \multirowsetup
1967         if (getPWidth(cell).zero())
1968                 cs.alignment = halign;
1969         else
1970                 cs.alignment = LYX_ALIGN_LEFT;
1971
1972         // Multirows cannot have multiple paragraphs
1973         if (getPWidth(cell).zero()) {
1974                 toggleFixedWidth(cur, cellInset(cell).get(),
1975                                  !getPWidth(cell).zero(),
1976                                  isMultiColumn(cell), isMultiRow(cell));
1977                 // cur can become invalid after paragraphs were merged
1978                 cur.fixIfBroken();
1979         }
1980
1981         // set the bottom line of the last selected cell
1982         setBottomLine(cell, bottom_border);
1983
1984         for (idx_type i = 1; i < number; ++i) {
1985                 CellData & cs1 = cell_info[row + i][col];
1986                 cs1.multirow = CELL_PART_OF_MULTIROW;
1987                 cs.inset->appendParagraphs(cs1.inset->paragraphs());
1988                 cs1.inset->clear();
1989         }
1990         updateIndexes();
1991         return cell;
1992 }
1993
1994
1995 Tabular::idx_type Tabular::columnSpan(idx_type cell) const
1996 {
1997         row_type const row = cellRow(cell);
1998         col_type const col = cellColumn(cell);
1999         int span = 1;
2000         while (col + span < ncols() && isPartOfMultiColumn(row, col + span))
2001                 ++span;
2002
2003         return span;
2004 }
2005
2006
2007 Tabular::idx_type Tabular::rowSpan(idx_type cell) const
2008 {
2009         col_type const column = cellColumn(cell);
2010         col_type row = cellRow(cell) + 1;
2011         while (row < nrows() && isPartOfMultiRow(row, column))
2012                 ++row;
2013
2014         return row - cellRow(cell);
2015 }
2016
2017
2018 void Tabular::unsetMultiColumn(idx_type cell)
2019 {
2020         if (!isMultiColumn(cell))
2021                 return;
2022
2023         row_type const row = cellRow(cell);
2024         col_type const col = cellColumn(cell);
2025         row_type const span = columnSpan(cell);
2026         for (col_type c = 0; c < span; ++c) {
2027                 // in the table dialog the lines are set in every case
2028                 // when unsetting a multicolumn this leads to an additional right
2029                 // line for every cell that was part of the former multicolumn cell,
2030                 // except if the cell is in the last column
2031                 // therefore remove this line
2032                 if (cell_info[row][col + c].multicolumn == CELL_BEGIN_OF_MULTICOLUMN
2033                         && (col + c) < (col + span - 1))
2034                         cell_info[row][col + c].right_line = false;
2035                 cell_info[row][col + c].multicolumn = CELL_NORMAL;
2036         }
2037         updateIndexes();
2038 }
2039
2040
2041 void Tabular::unsetMultiRow(idx_type cell)
2042 {
2043         if (!isMultiRow(cell))
2044                 return;
2045
2046         cellInfo(cell).valignment = LYX_VALIGN_TOP;
2047         cellInfo(cell).alignment = LYX_ALIGN_CENTER;
2048         row_type const row = cellRow(cell);
2049         col_type const col = cellColumn(cell);
2050         row_type const span = rowSpan(cell);
2051         for (row_type r = 0; r < span; ++r)
2052                 cell_info[row + r][col].multirow = CELL_NORMAL;
2053         updateIndexes();
2054 }
2055
2056
2057 void Tabular::setRotateCell(idx_type cell, int value)
2058 {
2059         cellInfo(cell).rotate = value;
2060 }
2061
2062
2063 int Tabular::getRotateCell(idx_type cell) const
2064 {
2065         return cellInfo(cell).rotate;
2066 }
2067
2068
2069 bool Tabular::needRotating() const
2070 {
2071         if (rotate && !is_long_tabular)
2072                 return true;
2073         for (row_type r = 0; r < nrows(); ++r)
2074                 for (col_type c = 0; c < ncols(); ++c)
2075                         if (cell_info[r][c].rotate != 0)
2076                                 return true;
2077         return false;
2078 }
2079
2080
2081 bool Tabular::isLastCell(idx_type cell) const
2082 {
2083         if (cell + 1 < numberofcells)
2084                 return false;
2085         return true;
2086 }
2087
2088
2089 Tabular::idx_type Tabular::cellAbove(idx_type cell) const
2090 {
2091         if (cellRow(cell) == 0)
2092                 return cell;
2093
2094         col_type const col = cellColumn(cell);
2095         row_type r = cellRow(cell) - 1;
2096         while (r > 0 && cell_info[r][col].multirow == CELL_PART_OF_MULTIROW)
2097                 --r;
2098
2099         return cell_info[r][col].cellno;
2100 }
2101
2102
2103 Tabular::idx_type Tabular::cellBelow(idx_type cell) const
2104 {
2105         row_type const nextrow = cellRow(cell) + rowSpan(cell);
2106         if (nextrow < nrows())
2107                 return cell_info[nextrow][cellColumn(cell)].cellno;
2108         return cell;
2109 }
2110
2111
2112 Tabular::idx_type Tabular::cellIndex(row_type row, col_type column) const
2113 {
2114         LASSERT(column != npos && column < ncols(), column = 0);
2115         LASSERT(row != npos && row < nrows(), row = 0);
2116         return cell_info[row][column].cellno;
2117 }
2118
2119
2120 void Tabular::setUsebox(idx_type cell, BoxType type)
2121 {
2122         cellInfo(cell).usebox = type;
2123 }
2124
2125
2126 Tabular::BoxType Tabular::getUsebox(idx_type cell) const
2127 {
2128         if (getRotateCell(cell) == 0
2129             && ((!column_info[cellColumn(cell)].p_width.zero() && !isMultiColumn(cell)) ||
2130                 (isMultiColumn(cell) && !cellInfo(cell).p_width.zero())))
2131                 return BOX_NONE;
2132         if (cellInfo(cell).usebox > 1)
2133                 return cellInfo(cell).usebox;
2134         return useBox(cell);
2135 }
2136
2137
2138 ///
2139 //  This are functions used for the longtable support
2140 ///
2141 void Tabular::setLTHead(row_type row, bool flag, ltType const & hd,
2142                            bool first)
2143 {
2144         if (first) {
2145                 endfirsthead = hd;
2146                 if (hd.set)
2147                         row_info[row].endfirsthead = flag;
2148         } else {
2149                 endhead = hd;
2150                 if (hd.set)
2151                         row_info[row].endhead = flag;
2152         }
2153 }
2154
2155
2156 bool Tabular::getRowOfLTHead(row_type row, ltType & hd) const
2157 {
2158         hd = endhead;
2159         hd.set = haveLTHead();
2160         return row_info[row].endhead;
2161 }
2162
2163
2164 bool Tabular::getRowOfLTFirstHead(row_type row, ltType & hd) const
2165 {
2166         hd = endfirsthead;
2167         hd.set = haveLTFirstHead();
2168         return row_info[row].endfirsthead;
2169 }
2170
2171
2172 void Tabular::setLTFoot(row_type row, bool flag, ltType const & fd,
2173                            bool last)
2174 {
2175         if (last) {
2176                 endlastfoot = fd;
2177                 if (fd.set)
2178                         row_info[row].endlastfoot = flag;
2179         } else {
2180                 endfoot = fd;
2181                 if (fd.set)
2182                         row_info[row].endfoot = flag;
2183         }
2184 }
2185
2186
2187 bool Tabular::getRowOfLTFoot(row_type row, ltType & fd) const
2188 {
2189         fd = endfoot;
2190         fd.set = haveLTFoot();
2191         return row_info[row].endfoot;
2192 }
2193
2194
2195 bool Tabular::getRowOfLTLastFoot(row_type row, ltType & fd) const
2196 {
2197         fd = endlastfoot;
2198         fd.set = haveLTLastFoot();
2199         return row_info[row].endlastfoot;
2200 }
2201
2202
2203 void Tabular::setLTNewPage(row_type row, bool what)
2204 {
2205         row_info[row].newpage = what;
2206 }
2207
2208
2209 bool Tabular::getLTNewPage(row_type row) const
2210 {
2211         return row_info[row].newpage;
2212 }
2213
2214
2215 bool Tabular::haveLTHead(bool withcaptions) const
2216 {
2217         if (!is_long_tabular)
2218                 return false;
2219         for (row_type i = 0; i < nrows(); ++i)
2220                 if (row_info[i].endhead &&
2221                     (withcaptions || !row_info[i].caption))
2222                         return true;
2223         return false;
2224 }
2225
2226
2227 bool Tabular::haveLTFirstHead(bool withcaptions) const
2228 {
2229         if (!is_long_tabular || endfirsthead.empty)
2230                 return false;
2231         for (row_type r = 0; r < nrows(); ++r)
2232                 if (row_info[r].endfirsthead &&
2233                     (withcaptions || !row_info[r].caption))
2234                         return true;
2235         return false;
2236 }
2237
2238
2239 bool Tabular::haveLTFoot(bool withcaptions) const
2240 {
2241         if (!is_long_tabular)
2242                 return false;
2243         for (row_type r = 0; r < nrows(); ++r)
2244                 if (row_info[r].endfoot &&
2245                     (withcaptions || !row_info[r].caption))
2246                         return true;
2247         return false;
2248 }
2249
2250
2251 bool Tabular::haveLTLastFoot(bool withcaptions) const
2252 {
2253         if (!is_long_tabular || endlastfoot.empty)
2254                 return false;
2255         for (row_type r = 0; r < nrows(); ++r)
2256                 if (row_info[r].endlastfoot &&
2257                     (withcaptions || !row_info[r].caption))
2258                         return true;
2259         return false;
2260 }
2261
2262
2263 Tabular::idx_type Tabular::setLTCaption(Cursor & cur, row_type row, bool what)
2264 {
2265         idx_type i = getFirstCellInRow(row);
2266         if (what) {
2267                 setMultiColumn(cur, i, numberOfCellsInRow(row), false);
2268                 setTopLine(i, false);
2269                 setBottomLine(i, false);
2270                 setLeftLine(i, false);
2271                 setRightLine(i, false);
2272                 if (!row_info[row].endfirsthead && !row_info[row].endhead &&
2273                     !row_info[row].endfoot && !row_info[row].endlastfoot) {
2274                         setLTHead(row, true, endfirsthead, true);
2275                         row_info[row].endfirsthead = true;
2276                 }
2277         } else {
2278                 unsetMultiColumn(i);
2279                 // When unsetting a caption row, also all existing
2280                 // captions in this row must be dissolved.
2281         }
2282         row_info[row].caption = what;
2283         return i;
2284 }
2285
2286
2287 bool Tabular::ltCaption(row_type row) const
2288 {
2289         return row_info[row].caption;
2290 }
2291
2292
2293 bool Tabular::haveLTCaption(CaptionType captiontype) const
2294 {
2295         if (!is_long_tabular)
2296                 return false;
2297         for (row_type r = 0; r < nrows(); ++r) {
2298                 if (row_info[r].caption) {
2299                         switch (captiontype) {
2300                         case CAPTION_FIRSTHEAD:
2301                                 if (row_info[r].endfirsthead)
2302                                         return true;
2303                                 break;
2304                         case CAPTION_HEAD:
2305                                 if (row_info[r].endhead)
2306                                         return true;
2307                                 break;
2308                         case CAPTION_FOOT:
2309                                 if (row_info[r].endfoot)
2310                                         return true;
2311                                 break;
2312                         case CAPTION_LASTFOOT:
2313                                 if (row_info[r].endlastfoot)
2314                                         return true;
2315                                 break;
2316                         case CAPTION_ANY:
2317                                 return true;
2318                         }
2319                 }
2320         }
2321         return false;
2322 }
2323
2324
2325 // end longtable support functions
2326
2327 void Tabular::setRowAscent(row_type row, int height)
2328 {
2329         if (row >= nrows() || row_info[row].ascent == height)
2330                 return;
2331         row_info[row].ascent = height;
2332 }
2333
2334
2335 void Tabular::setRowDescent(row_type row, int height)
2336 {
2337         if (row >= nrows() || row_info[row].descent == height)
2338                 return;
2339         row_info[row].descent = height;
2340 }
2341
2342
2343 int Tabular::rowAscent(row_type row) const
2344 {
2345         LASSERT(row < nrows(), row = 0);
2346         return row_info[row].ascent;
2347 }
2348
2349
2350 int Tabular::rowDescent(row_type row) const
2351 {
2352         LASSERT(row < nrows(), row = 0);
2353         return row_info[row].descent;
2354 }
2355
2356
2357 int Tabular::height() const
2358 {
2359         int height = 0;
2360         for (row_type row = 0; row < nrows(); ++row)
2361                 height += rowAscent(row) + rowDescent(row) +
2362                         interRowSpace(row);
2363         return height;
2364 }
2365
2366
2367 bool Tabular::isPartOfMultiColumn(row_type row, col_type column) const
2368 {
2369         LASSERT(row < nrows(), return false);
2370         LASSERT(column < ncols(), return false);
2371         return cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN;
2372 }
2373
2374
2375 bool Tabular::isPartOfMultiRow(row_type row, col_type column) const
2376 {
2377         LASSERT(row < nrows(), return false);
2378         LASSERT(column < ncols(), return false);
2379         return cell_info[row][column].multirow == CELL_PART_OF_MULTIROW;
2380 }
2381
2382
2383 void Tabular::TeXTopHLine(otexstream & os, row_type row, list<col_type> columns) const
2384 {
2385         // we only output complete row lines and the 1st row here, the rest
2386         // is done in Tabular::TeXBottomHLine(...)
2387
2388         // get for each column the topline (if any)
2389         map<col_type, bool> topline, topltrims, toprtrims;
2390         col_type nset = 0;
2391         bool have_trims = false;
2392         for (auto const & c : columns) {
2393                 topline[c] = topLine(cellIndex(row, c));
2394                 topltrims[c] = topLineTrim(cellIndex(row, c)).first;
2395                 toprtrims[c] = topLineTrim(cellIndex(row, c)).second;
2396                 // If cell is part of a multirow and not the first cell of the
2397                 // multirow, no line must be drawn.
2398                 if (row != 0)
2399                         if (isMultiRow(cellIndex(row, c))
2400                             && cell_info[row][c].multirow != CELL_BEGIN_OF_MULTIROW) {
2401                                 topline[c] = false;
2402                                 topltrims[c] = false;
2403                                 toprtrims[c] = false;
2404                         }
2405                 // copy trimming to multicolumn parts
2406                 if (isPartOfMultiColumn(row, c)) {
2407                         topltrims[c] = topltrims[c-1];
2408                         toprtrims[c] = toprtrims[c-1];
2409                 }
2410                 if (topline.find(c) != topline.end() && topline.find(c)->second)
2411                         ++nset;
2412                 if ((topltrims.find(c) != topltrims.end() && topltrims.find(c)->second)
2413                      || (toprtrims.find(c) != toprtrims.end() && toprtrims.find(c)->second))
2414                         have_trims = true;
2415         }
2416
2417         // do nothing if empty first row, or incomplete row line after
2418         if ((row == 0 && nset == 0) || (row > 0 && nset != ncols()))
2419                 return;
2420
2421         // Is this the actual first row (excluding longtable caption row)?
2422         bool const realfirstrow = (row == 0
2423                                    || (is_long_tabular && row == 1 && ltCaption(0)));
2424
2425         // only output complete row lines and the 1st row's clines
2426         if (nset == ncols() && !have_trims) {
2427                 if (use_booktabs) {
2428                         os << (realfirstrow ? "\\toprule " : "\\midrule ");
2429                 } else {
2430                         os << "\\hline ";
2431                 }
2432         } else if (realfirstrow || have_trims) {
2433                 string const cline = use_booktabs ? "\\cmidrule" : "\\cline";
2434                 col_type c = 0;
2435                 for (auto & cl : columns) {
2436                         if (cl < c)
2437                                 continue;
2438                         c = cl;
2439                         if (topline.find(c)->second) {
2440                                 col_type offset = 0;
2441                                 for (col_type j = 0 ; j < c; ++j)
2442                                         if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
2443                                                 ++offset;
2444                                 string const firstcol = convert<string>(c + 1 + offset);
2445                                 while (isPartOfMultiColumn(row, c))
2446                                         ++c;
2447                                 string trim;
2448                                 if (topltrims.find(c) != topltrims.end()
2449                                      && topltrims.find(c)->second)
2450                                         trim = "l";
2451                                 col_type cstart = c;
2452                                 for ( ; c < ncols() - 1 && topline.find(c + 1)->second ; ++c) {
2453                                         if (isMultiColumn(cellIndex(row, c))
2454                                             && c < ncols() - 1 && isPartOfMultiColumn(row, c + 1))
2455                                                 continue;
2456                                         if (c > cstart && topltrims.find(c) != topltrims.end()
2457                                                         && topltrims.find(c)->second) {
2458                                                 if (!isPartOfMultiColumn(row, c))
2459                                                         --c;
2460                                                 break;
2461                                         } else if (toprtrims.find(c) != toprtrims.end()
2462                                                    && toprtrims.find(c)->second)
2463                                                 break;
2464                                 }
2465
2466                                 for (col_type j = cstart ; j < c ; ++j)
2467                                         if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
2468                                                 ++offset;
2469                                 col_type const lastcol = c + 1 + offset;
2470                                 if (toprtrims.find(c) != toprtrims.end()
2471                                     && toprtrims.find(c)->second)
2472                                         trim += "r";
2473
2474                                 os << cline;
2475                                 if (!trim.empty())
2476                                         os << "(" << trim << ")";
2477                                 os << "{" << firstcol << '-' << lastcol << "}";
2478                                 if (c == ncols() - 1)
2479                                         break;
2480                                 ++c;
2481                         }
2482                 }
2483         }
2484         os << "\n";
2485 }
2486
2487
2488 void Tabular::TeXBottomHLine(otexstream & os, row_type row, list<col_type> columns) const
2489 {
2490         // we output bottomlines of row r and the toplines of row r+1
2491         // if the latter do not span the whole tabular
2492
2493         // get the bottomlines of row r, and toplines in next row
2494         bool lastrow = row == nrows() - 1;
2495         map<col_type, bool> bottomline, topline, topltrims, toprtrims, bottomltrims, bottomrtrims;
2496         bool nextrowset = true;
2497         for (auto const & c : columns) {
2498                 idx_type const idx = cellIndex(row, c);
2499                 bottomline[c] = bottomLine(cellIndex(row, c));
2500                 bottomltrims[c] = bottomLineTrim(idx).first;
2501                 bottomrtrims[c] = bottomLineTrim(idx).second;
2502                 topline[c] =  !lastrow && topLine(cellIndex(row + 1, c));
2503                 topltrims[c] = !lastrow && topLineTrim(cellIndex(row + 1, c)).first;
2504                 toprtrims[c] = !lastrow && topLineTrim(cellIndex(row + 1, c)).second;
2505                 // If cell is part of a multirow and not the last cell of the
2506                 // multirow, no line must be drawn.
2507                 if (!lastrow)
2508                         if (isMultiRow(cellIndex(row, c))
2509                             && isMultiRow(cellIndex(row + 1, c))
2510                             && cell_info[row + 1][c].multirow != CELL_BEGIN_OF_MULTIROW) {
2511                                 bottomline[c] = false;
2512                                 topline[c] = false;
2513                                 bottomltrims[c] = false;
2514                                 bottomrtrims[c] = false;
2515                                 topltrims[c] = false;
2516                                 toprtrims[c] = false;
2517                         }
2518                 // copy trimming in multicolumn parts
2519                 if (isPartOfMultiColumn(row, c)) {
2520                         topltrims[c] = topltrims[c-1];
2521                         toprtrims[c] = toprtrims[c-1];
2522                         bottomltrims[c] = bottomltrims[c-1];
2523                         bottomrtrims[c] = bottomrtrims[c-1];
2524                 }
2525                         
2526                 nextrowset &= topline.find(c) != topline.end() && topline.find(c)->second;
2527         }
2528
2529         // combine this row's bottom lines and next row's toplines if necessary
2530         col_type nset = 0;
2531         bool have_trims = false;
2532         for (auto const & c : columns) {
2533                 if (!nextrowset)
2534                         bottomline[c] = bottomline.find(c)->second || topline.find(c)->second;
2535                 bottomltrims[c] = (bottomltrims.find(c) != bottomltrims.end() && bottomltrims.find(c)->second)
2536                                 || (topltrims.find(c) != topltrims.end() && topltrims.find(c)->second);
2537                 bottomrtrims[c] =(bottomrtrims.find(c) != bottomrtrims.end() && bottomrtrims.find(c)->second)
2538                                 || (toprtrims.find(c) != toprtrims.end() && toprtrims.find(c)->second);
2539                 if (bottomline.find(c)->second)
2540                         ++nset;
2541                 if ((bottomltrims.find(c) != bottomltrims.end() && bottomltrims.find(c)->second)
2542                      || (bottomrtrims.find(c) != bottomrtrims.end() && bottomrtrims.find(c)->second))
2543                         have_trims = true;
2544         }
2545
2546         // do nothing if empty, OR incomplete row line with a topline in next row
2547         if (nset == 0 || (nextrowset && nset != ncols()))
2548                 return;
2549
2550         if (nset == ncols() && !have_trims) {
2551                 if (use_booktabs)
2552                         os << (lastrow ? "\\bottomrule" : "\\midrule");
2553                 else
2554                         os << "\\hline ";
2555         } else {
2556                 string const cline = use_booktabs ? "\\cmidrule" : "\\cline";
2557                 col_type c = 0;
2558                 for (auto & cl : columns) {
2559                         if (cl < c)
2560                                 continue;
2561                         c = cl;
2562                         if (bottomline.find(c)->second) {
2563                                 col_type offset = 0;
2564                                 for (col_type j = 0 ; j < c; ++j)
2565                                         if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
2566                                                 ++offset;
2567                                 string const firstcol = convert<string>(c + 1 + offset);
2568                                 while (isPartOfMultiColumn(row, c))
2569                                         ++c;
2570                                 string trim;
2571                                 if (bottomltrims.find(c) != bottomltrims.end()
2572                                      && bottomltrims.find(c)->second)
2573                                         trim = "l";
2574                                 col_type cstart = c;
2575                                 for ( ; c < ncols() - 1 && bottomline.find(c + 1)->second ; ++c) {
2576                                         if (isMultiColumn(cellIndex(row, c))
2577                                             && c < ncols() - 1
2578                                             && isPartOfMultiColumn(row, c + 1))
2579                                                 continue;
2580                                         if (c > cstart
2581                                             && bottomltrims.find(c) != bottomltrims.end()
2582                                             && bottomltrims.find(c)->second) {
2583                                                 if (!isPartOfMultiColumn(row, c))
2584                                                         --c;
2585                                                 break;
2586                                         } else if (bottomrtrims.find(c) != bottomrtrims.end()
2587                                                    && bottomrtrims.find(c)->second)
2588                                                 break;
2589                                 }
2590
2591                                 for (col_type j = cstart ; j < c ; ++j)
2592                                         if (column_info[j].alignment == LYX_ALIGN_DECIMAL)
2593                                                 ++offset;
2594                                 col_type const lastcol = c + 1 + offset;
2595                                 if (bottomrtrims.find(c) != bottomrtrims.end()
2596                                     && bottomrtrims.find(c)->second)
2597                                         trim += "r";
2598
2599                                 os << cline;
2600                                 if (!trim.empty())
2601                                         os << "(" << trim << ")";
2602                                 os << "{" << firstcol << '-' << lastcol << "}";
2603                                 if (c == ncols() - 1)
2604                                         break;
2605                                 ++c;
2606                         }
2607                 }
2608         }
2609         os << "\n";
2610 }
2611
2612
2613 void Tabular::TeXCellPreamble(otexstream & os, idx_type cell,
2614                               bool & ismulticol, bool & ismultirow,
2615                               bool const bidi) const
2616 {
2617         row_type const r = cellRow(cell);
2618         if (is_long_tabular && row_info[r].caption)
2619                 return;
2620
2621         Tabular::VAlignment valign =  getVAlignment(cell, !isMultiColumn(cell));
2622         LyXAlignment align = getAlignment(cell, !isMultiColumn(cell));
2623         // figure out how to set the lines
2624         // we always set double lines to the right of the cell
2625         // or left in bidi RTL, respectively.
2626         col_type const c = cellColumn(cell);
2627         col_type const nextcol = c + columnSpan(cell);
2628         bool const decimal = column_info[c].alignment == LYX_ALIGN_DECIMAL;
2629         bool colright = columnRightLine(c);
2630         bool colleft = columnLeftLine(c);
2631         bool nextcolleft = nextcol < ncols() && columnLeftLine(nextcol);
2632         bool nextcellleft = nextcol < ncols()
2633                 && leftLine(cellIndex(r, nextcol));
2634         bool coldouble = colright && nextcolleft;
2635         bool celldouble = rightLine(cell) && nextcellleft;
2636
2637         ismulticol = (isMultiColumn(cell)
2638                       || (c == 0 && colleft != leftLine(cell))
2639                       || ((colright || nextcolleft) && !rightLine(cell) && !nextcellleft)
2640                       || (!colright && !nextcolleft && (rightLine(cell) || nextcellleft))
2641                       || (coldouble != celldouble))
2642                      && !decimal;
2643
2644         // we center in multicol when no decimal point
2645         if (decimal) {
2646                 docstring const align_d = column_info[c].decimal_point;
2647                 DocIterator const dit = separatorPos(cellInset(cell), align_d);
2648                 bool const nosep = !dit;
2649                 ismulticol |= nosep;
2650                 celldouble &= nosep;
2651         }
2652
2653         // up counter by 1 for each decimally aligned col since they use 2 latex cols
2654         int latexcolspan = columnSpan(cell);
2655         for (col_type col = c; col < c + columnSpan(cell); ++col)
2656                 if (column_info[col].alignment == LYX_ALIGN_DECIMAL)
2657                         ++latexcolspan;
2658
2659         if (ismulticol) {
2660                 os << "\\multicolumn{" << latexcolspan << "}{";
2661                 if (((bidi && c == getLastCellInRow(cellRow(0)) && rightLine(cell))
2662                      || (!bidi && c == 0 && leftLine(cell))))
2663                         os << '|';
2664                 if (bidi && celldouble)
2665                         // add extra vertical line if we want a double one
2666                         os << '|';
2667                 if (!cellInfo(cell).align_special.empty()) {
2668                         os << cellInfo(cell).align_special;
2669                 } else {
2670                         if (!getPWidth(cell).zero()) {
2671                                 switch (align) {
2672                                 case LYX_ALIGN_LEFT:
2673                                         os << ">{\\raggedright}";
2674                                         break;
2675                                 case LYX_ALIGN_RIGHT:
2676                                         os << ">{\\raggedleft}";
2677                                         break;
2678                                 case LYX_ALIGN_CENTER:
2679                                         os << ">{\\centering}";
2680                                         break;
2681                                 default:
2682                                         break;
2683                                 }
2684                                 switch (valign) {
2685                                 case LYX_VALIGN_TOP:
2686                                         os << 'p';
2687                                         break;
2688                                 case LYX_VALIGN_MIDDLE:
2689                                         os << 'm';
2690                                         break;
2691                                 case LYX_VALIGN_BOTTOM:
2692                                         os << 'b';
2693                                         break;
2694                                 }
2695                                 os << '{'
2696                                    << from_ascii(getPWidth(cell).asLatexString())
2697                                    << '}';
2698                         } else {
2699                                 switch (align) {
2700                                 case LYX_ALIGN_LEFT:
2701                                         os << 'l';
2702                                         break;
2703                                 case LYX_ALIGN_RIGHT:
2704                                         os << 'r';
2705                                         break;
2706                                 default:
2707                                         os << 'c';
2708                                         break;
2709                                 }
2710                         } // end if else !getPWidth
2711                 } // end if else !cellinfo_of_cell
2712                 if ((bidi && leftLine(cell)) || (!bidi && rightLine(cell)) || nextcellleft)
2713                         os << '|';
2714                 if (!bidi && celldouble)
2715                         // add extra vertical line if we want a double one
2716                         os << '|';
2717                 os << "}{";
2718         } // end if ismulticol
2719
2720         // we only need code for the first multirow cell
2721         ismultirow = isMultiRow(cell);
2722         if (ismultirow) {
2723                 os << "\\multirow{" << rowSpan(cell) << "}{";
2724                 if (!getPWidth(cell).zero())
2725                         os << from_ascii(getPWidth(cell).asLatexString());
2726                 else
2727                         // we need to set a default value
2728                         os << "*";
2729                 os << "}";
2730                 if (!getMROffset(cell).zero())
2731                         os << "[" << from_ascii(getMROffset(cell).asLatexString()) << "]";
2732                 os << "{";
2733         } // end if ismultirow
2734
2735         if (getRotateCell(cell) != 0)
2736                 os << "\\begin{turn}{" << convert<string>(getRotateCell(cell)) << "}\n";
2737
2738         if (getUsebox(cell) == BOX_PARBOX) {
2739                 os << "\\parbox[";
2740                 switch (valign) {
2741                 case LYX_VALIGN_TOP:
2742                         os << 't';
2743                         break;
2744                 case LYX_VALIGN_MIDDLE:
2745                         os << 'c';
2746                         break;
2747                 case LYX_VALIGN_BOTTOM:
2748                         os << 'b';
2749                         break;
2750                 }
2751                 os << "]{" << from_ascii(getPWidth(cell).asLatexString())
2752                    << "}{";
2753         } else if (getUsebox(cell) == BOX_MINIPAGE) {
2754                 os << "\\begin{minipage}[";
2755                 switch (valign) {
2756                 case LYX_VALIGN_TOP:
2757                         os << 't';
2758                         break;
2759                 case LYX_VALIGN_MIDDLE:
2760                         os << 'm';
2761                         break;
2762                 case LYX_VALIGN_BOTTOM:
2763                         os << 'b';
2764                         break;
2765                 }
2766                 os << "]{" << from_ascii(getPWidth(cell).asLatexString())
2767                    << "}\n";
2768         } else if (getRotateCell(cell) != 0 && getUsebox(cell) == BOX_VARWIDTH) {
2769                 os << "\\begin{varwidth}[";
2770                 switch (valign) {
2771                 case LYX_VALIGN_TOP:
2772                         os << 't';
2773                         break;
2774                 case LYX_VALIGN_MIDDLE:
2775                         os << 'm';
2776                         break;
2777                 case LYX_VALIGN_BOTTOM:
2778                         os << 'b';
2779                         break;
2780         }
2781         os << "]{\\linewidth}\n";
2782 }
2783 }
2784
2785
2786 void Tabular::TeXCellPostamble(otexstream & os, idx_type cell,
2787                                bool ismulticol, bool ismultirow) const
2788 {
2789         row_type const r = cellRow(cell);
2790         if (is_long_tabular && row_info[r].caption)
2791                 return;
2792
2793         // usual cells
2794         if (getUsebox(cell) == BOX_PARBOX)
2795                 os << '}';
2796         else if (getUsebox(cell) == BOX_MINIPAGE)
2797                 os << breakln << "\\end{minipage}";
2798         else if (getRotateCell(cell) != 0 && getUsebox(cell) == BOX_VARWIDTH)
2799                 os << breakln << "\\end{varwidth}";
2800         if (getRotateCell(cell) != 0)
2801                 os << breakln << "\\end{turn}";
2802         if (ismultirow)
2803                 os << '}';
2804         if (ismulticol)
2805                 os << '}';
2806 }
2807
2808
2809 void Tabular::TeXLongtableHeaderFooter(otexstream & os,
2810                                        OutputParams const & runparams,
2811                                        list<col_type> columns) const
2812 {
2813         if (!is_long_tabular)
2814                 return;
2815
2816         // caption handling
2817         // output caption which is in no header or footer
2818         if (haveLTCaption()) {
2819                 for (row_type r = 0; r < nrows(); ++r) {
2820                         if (row_info[r].caption &&
2821                             !row_info[r].endfirsthead && !row_info[r].endhead &&
2822                             !row_info[r].endfoot && !row_info[r].endlastfoot)
2823                                 TeXRow(os, r, runparams, columns);
2824                 }
2825         }
2826         // output first header info
2827         if (haveLTFirstHead()) {
2828                 if (endfirsthead.topDL)
2829                         os << "\\hline\n";
2830                 for (row_type r = 0; r < nrows(); ++r) {
2831                         if (row_info[r].endfirsthead)
2832                                 TeXRow(os, r, runparams, columns);
2833                 }
2834                 if (endfirsthead.bottomDL)
2835                         os << "\\hline\n";
2836                 os << "\\endfirsthead\n";
2837         }
2838         // output header info
2839         if (haveLTHead()) {
2840                 if (endfirsthead.empty && !haveLTFirstHead())
2841                         os << "\\endfirsthead\n";
2842                 if (endhead.topDL)
2843                         os << "\\hline\n";
2844                 for (row_type r = 0; r < nrows(); ++r) {
2845                         if (row_info[r].endhead)
2846                                 TeXRow(os, r, runparams, columns);
2847                 }
2848                 if (endhead.bottomDL)
2849                         os << "\\hline\n";
2850                 os << "\\endhead\n";
2851         }
2852         // output footer info
2853         if (haveLTFoot()) {
2854                 if (endfoot.topDL)
2855                         os << "\\hline\n";
2856                 for (row_type r = 0; r < nrows(); ++r) {
2857                         if (row_info[r].endfoot)
2858                                 TeXRow(os, r, runparams, columns);
2859                 }
2860                 if (endfoot.bottomDL)
2861                         os << "\\hline\n";
2862                 os << "\\endfoot\n";
2863                 if (endlastfoot.empty && !haveLTLastFoot())
2864                         os << "\\endlastfoot\n";
2865         }
2866         // output lastfooter info
2867         if (haveLTLastFoot()) {
2868                 if (endlastfoot.topDL)
2869                         os << "\\hline\n";
2870                 for (row_type r = 0; r < nrows(); ++r) {
2871                         if (row_info[r].endlastfoot)
2872                                 TeXRow(os, r, runparams, columns);
2873                 }
2874                 if (endlastfoot.bottomDL)
2875                         os << "\\hline\n";
2876                 os << "\\endlastfoot\n";
2877         }
2878 }
2879
2880
2881 bool Tabular::isValidRow(row_type row) const
2882 {
2883         if (!is_long_tabular)
2884                 return true;
2885         return !row_info[row].endhead && !row_info[row].endfirsthead
2886                 && !row_info[row].endfoot && !row_info[row].endlastfoot
2887                 && !row_info[row].caption;
2888 }
2889
2890
2891 void Tabular::TeXRow(otexstream & os, row_type row,
2892                      OutputParams const & runparams,
2893                      list<col_type> columns) const
2894 {
2895         idx_type cell = cellIndex(row, 0);
2896
2897         //output the top line
2898         TeXTopHLine(os, row, columns);
2899
2900         if (row_info[row].top_space_default) {
2901                 if (use_booktabs)
2902                         os << "\\addlinespace\n";
2903                 else
2904                         os << "\\noalign{\\vskip\\doublerulesep}\n";
2905         } else if(!row_info[row].top_space.zero()) {
2906                 if (use_booktabs)
2907                         os << "\\addlinespace["
2908                            << from_ascii(row_info[row].top_space.asLatexString())
2909                            << "]\n";
2910                 else {
2911                         os << "\\noalign{\\vskip"
2912                            << from_ascii(row_info[row].top_space.asLatexString())
2913                            << "}\n";
2914                 }
2915         }
2916         bool ismulticol = false;
2917         bool ismultirow = false;
2918
2919         // The bidi package (loaded by polyglossia with XeTeX) reverses RTL table columns
2920         // Luabibdi (used by LuaTeX) behaves like classic
2921         bool const bidi_rtl =
2922                 runparams.local_font->isRightToLeft()
2923                 && runparams.useBidiPackage();
2924         idx_type lastcell =
2925                 bidi_rtl ? getFirstCellInRow(row) : getLastCellInRow(row);
2926
2927         for (auto const & c : columns) {
2928                 if (isPartOfMultiColumn(row, c))
2929                         continue;
2930
2931                 cell = cellIndex(row, c);
2932
2933                 if (isPartOfMultiRow(row, c)
2934                     && column_info[c].alignment != LYX_ALIGN_DECIMAL) {
2935                         if (cell != lastcell)
2936                                 os << " & ";
2937                         continue;
2938                 }
2939
2940                 TeXCellPreamble(os, cell, ismulticol, ismultirow, bidi_rtl);
2941                 InsetTableCell const * inset = cellInset(cell);
2942
2943                 Paragraph const & par = inset->paragraphs().front();
2944
2945                 os.texrow().forceStart(par.id(), 0);
2946
2947                 bool rtl = par.isRTL(buffer().params())
2948                         && !par.empty()
2949                         && getPWidth(cell).zero()
2950                         && !runparams.use_polyglossia;
2951
2952                 if (rtl) {
2953                         string const lang =
2954                                 par.getParLanguage(buffer().params())->lang();
2955                         if (lang == "farsi")
2956                                 os << "\\textFR{";
2957                         else if (lang == "arabic_arabi")
2958                                 os << "\\textAR{";
2959                         // currently, remaining RTL languages are
2960                         // arabic_arabtex and hebrew
2961                         else
2962                                 os << "\\R{";
2963                 }
2964                 // pass to the OutputParams that we are in a cell and
2965                 // which alignment we have set.
2966                 // InsetNewline needs this context information.
2967                 OutputParams newrp(runparams);
2968                 newrp.inTableCell = (getAlignment(cell) == LYX_ALIGN_BLOCK)
2969                                     ? OutputParams::PLAIN
2970                                     : OutputParams::ALIGNED;
2971
2972                 if (getAlignment(cell) == LYX_ALIGN_DECIMAL) {
2973                         // copy cell and split in 2
2974                         InsetTableCell head = InsetTableCell(*cellInset(cell));
2975                         head.setBuffer(const_cast<Buffer &>(buffer()));
2976                         DocIterator dit = cellInset(cell)->getText(0)->macrocontextPosition();
2977                         dit.pop_back();
2978                         dit.push_back(CursorSlice(head));
2979                         head.setMacrocontextPositionRecursive(dit);
2980                         bool hassep = false;
2981                         InsetTableCell tail = splitCell(head, column_info[c].decimal_point, hassep);
2982                         if (hassep) {
2983                                 tail.setBuffer(head.buffer());
2984                                 dit.pop_back();
2985                                 dit.push_back(CursorSlice(tail));
2986                                 tail.setMacrocontextPositionRecursive(dit);
2987                         }
2988                         if (bidi_rtl) {
2989                                 if (hassep) {
2990                                         tail.latex(os, newrp);
2991                                         os << '&';
2992                                 }
2993                                 head.latex(os, newrp);
2994                         } else {
2995                                 head.latex(os, newrp);
2996                                 if (hassep) {
2997                                         os << '&';
2998                                         tail.latex(os, newrp);
2999                                 }
3000                         }
3001                 } else if (ltCaption(row)) {
3002                         // Inside longtable caption rows, we must only output the caption inset
3003                         // with its content and omit anything outside of that (see #10791)
3004                         InsetIterator it = inset_iterator_begin(*const_cast<InsetTableCell *>(inset));
3005                         InsetIterator i_end = inset_iterator_end(*const_cast<InsetTableCell *>(inset));
3006                         for (; it != i_end; ++it) {
3007                                 if (it->lyxCode() != CAPTION_CODE)
3008                                         continue;
3009                                 it->latex(os, runparams);
3010                                 break;
3011                         }
3012                 } else if (!isPartOfMultiRow(row, c)) {
3013                         if (!runparams.nice)
3014                                 os.texrow().start(par.id(), 0);
3015                         inset->latex(os, newrp);
3016                 }
3017
3018                 runparams.encoding = newrp.encoding;
3019                 if (rtl)
3020                         os << '}';
3021
3022                 TeXCellPostamble(os, cell, ismulticol, ismultirow);
3023                 if (cell != lastcell) { // not last cell in row
3024                         if (runparams.nice)
3025                                 os << " & ";
3026                         else
3027                                 os << " &\n";
3028                 }
3029         }
3030         os << "\\tabularnewline";
3031         if (row_info[row].bottom_space_default) {
3032                 if (use_booktabs)
3033                         os << "\\addlinespace";
3034                 else
3035                         os << "[\\doublerulesep]";
3036         } else if (!row_info[row].bottom_space.zero()) {
3037                 if (use_booktabs)
3038                         os << "\\addlinespace";
3039                 os << '['
3040                    << from_ascii(row_info[row].bottom_space.asLatexString())
3041                    << ']';
3042         }
3043         os << '\n';
3044
3045         //output the bottom line
3046         TeXBottomHLine(os, row, columns);
3047
3048         if (row_info[row].interline_space_default) {
3049                 if (use_booktabs)
3050                         os << "\\addlinespace\n";
3051                 else
3052                         os << "\\noalign{\\vskip\\doublerulesep}\n";
3053         } else if (!row_info[row].interline_space.zero()) {
3054                 if (use_booktabs)
3055                         os << "\\addlinespace["
3056                            << from_ascii(row_info[row].interline_space.asLatexString())
3057                            << "]\n";
3058                 else
3059                         os << "\\noalign{\\vskip"
3060                            << from_ascii(row_info[row].interline_space.asLatexString())
3061                            << "}\n";
3062         }
3063 }
3064
3065
3066 void Tabular::latex(otexstream & os, OutputParams const & runparams) const
3067 {
3068         bool const is_tabular_star = !is_long_tabular && !tabular_width.zero()
3069                 && !hasVarwidthColumn();
3070         bool const is_xltabular = is_long_tabular
3071                 && (hasVarwidthColumn() || !tabular_width.zero());
3072         TexRow::RowEntry pos = TexRow::textEntry(runparams.lastid, runparams.lastpos);
3073
3074         //+---------------------------------------------------------------------
3075         //+                      first the opening preamble                    +
3076         //+---------------------------------------------------------------------
3077
3078         os << safebreakln;
3079         if (!TexRow::isNone(pos))
3080                 os.texrow().start(pos);
3081
3082         if (rotate != 0) {
3083                 if (is_long_tabular)
3084                         os << "\\begin{landscape}\n";
3085                 else
3086                         os << "\\begin{turn}{" << convert<string>(rotate) << "}\n";
3087         }
3088
3089         // The bidi package (loaded by polyglossia with XeTeX) swaps the column
3090         // order for RTL (#9686). Thus we use this list.
3091         bool const bidi_rtl =
3092                 runparams.local_font->isRightToLeft()
3093                 && runparams.useBidiPackage();
3094         list<col_type> columns;
3095         for (col_type cl = 0; cl < ncols(); ++cl) {
3096                 if (bidi_rtl)
3097                         columns.push_front(cl);
3098                 else
3099                         columns.push_back(cl);
3100         }
3101
3102         // If we use \cline or \cmidrule, we need to locally de-activate
3103         // the - character when using languages that activate it (e.g., Czech, Slovak).
3104         bool deactivate_chars = false;
3105         if ((runparams.use_babel || runparams.use_polyglossia)
3106             && contains(runparams.active_chars, '-')) {
3107                 bool have_clines = false;
3108                 // Check if we use \cline or \cmidrule
3109                 for (row_type row = 0; row < nrows(); ++row) {
3110                         col_type bset = 0, tset = 0;
3111                         for (auto const & c : columns) {
3112                                 idx_type const idx = cellIndex(row, c);
3113                                 if (bottomLineTrim(idx).first || bottomLineTrim(idx).second
3114                                     || topLineTrim(idx).first || topLineTrim(idx).second) {
3115                                         have_clines = true;
3116                                         break;
3117                                 }
3118                                 if (bottomLine(cellIndex(row, c)))
3119                                         ++bset;
3120                                 if (topLine(cellIndex(row, c)))
3121                                         ++tset;
3122                         }
3123                         if ((bset > 0 && bset < ncols()) || (tset > 0 && tset < ncols())) {
3124                                 have_clines = true;
3125                                 break;
3126                         }
3127                 }
3128                 if (have_clines) {
3129                         deactivate_chars = true;
3130                         os << "\\begingroup\n"
3131                            << "\\catcode`\\-=12\n";
3132                 }
3133         }
3134
3135         if (is_long_tabular) {
3136                 if (is_xltabular)
3137                         os << "\\begin{xltabular}";
3138                 else
3139                         os << "\\begin{longtable}";
3140                 switch (longtabular_alignment) {
3141                 case LYX_LONGTABULAR_ALIGN_LEFT:
3142                         os << "[l]";
3143                         break;
3144                 case LYX_LONGTABULAR_ALIGN_CENTER:
3145                         os << "[c]";
3146                         break;
3147                 case LYX_LONGTABULAR_ALIGN_RIGHT:
3148                         os << "[r]";
3149                         break;
3150                 }
3151                 if (is_xltabular) {
3152                         if (tabular_width.zero())
3153                                 os << "{" << from_ascii("\\columnwidth") << "}";
3154                         else
3155                                 os << "{" << from_ascii(tabular_width.asLatexString()) << "}";
3156                 }
3157         } else {
3158                 if (is_tabular_star)
3159                         os << "\\begin{tabular*}{" << from_ascii(tabular_width.asLatexString()) << "}";
3160                 else if (hasVarwidthColumn()) {
3161                         os << "\\begin{tabularx}{";
3162                         if (tabular_width.zero())
3163                                 os << from_ascii("\\columnwidth") << "}";
3164                         else
3165                                 os << from_ascii(tabular_width.asLatexString()) << "}";
3166                 } else
3167                         os << "\\begin{tabular}";
3168                 switch (tabular_valignment) {
3169                 case LYX_VALIGN_TOP:
3170                         os << "[t]";
3171                         break;
3172                 case LYX_VALIGN_MIDDLE:
3173                         break;
3174                 case LYX_VALIGN_BOTTOM:
3175                         os << "[b]";
3176                         break;
3177                 }
3178         }
3179
3180         os << "{";
3181
3182         if (is_tabular_star)
3183                 os << "@{\\extracolsep{\\fill}}";
3184
3185         for (auto const & c : columns) {
3186                 if ((bidi_rtl && columnRightLine(c)) || (!bidi_rtl && columnLeftLine(c)))
3187                         os << '|';
3188                 if (!column_info[c].align_special.empty()) {
3189                         os << column_info[c].align_special;
3190                 } else {
3191                         if (!column_info[c].p_width.zero()) {
3192                                 bool decimal = false;
3193                                 switch (column_info[c].alignment) {
3194                                 case LYX_ALIGN_LEFT:
3195                                         os << ">{\\raggedright}";
3196                                         break;
3197                                 case LYX_ALIGN_RIGHT:
3198                                         os << ">{\\raggedleft}";
3199                                         break;
3200                                 case LYX_ALIGN_CENTER:
3201                                         os << ">{\\centering}";
3202                                         break;
3203                                 case LYX_ALIGN_NONE:
3204                                 case LYX_ALIGN_BLOCK:
3205                                 case LYX_ALIGN_LAYOUT:
3206                                 case LYX_ALIGN_SPECIAL:
3207                                         break;
3208                                 case LYX_ALIGN_DECIMAL: {
3209                                         if (bidi_rtl)
3210                                                 os << ">{\\raggedright}";
3211                                         else
3212                                                 os << ">{\\raggedleft}";
3213                                         decimal = true;
3214                                         break;
3215                                 }
3216                                 }
3217
3218                                 char valign = 'p';
3219                                 switch (column_info[c].valignment) {
3220                                 case LYX_VALIGN_TOP:
3221                                         // this is the default
3222                                         break;
3223                                 case LYX_VALIGN_MIDDLE:
3224                                         valign = 'm';
3225                                         break;
3226                                 case LYX_VALIGN_BOTTOM:
3227                                         valign = 'b';
3228                                         break;
3229                                 }
3230                                 os << valign;
3231
3232                                 // Fixed-width cells with alignment at decimal separator
3233                                 // are output as two cells of half the width with the decimal
3234                                 // separator as column sep. This effectively puts the content
3235                                 // centered, which differs from the normal decimal sep alignment
3236                                 // and is not ideal, but we cannot do better ATM (see #9568).
3237                                 // FIXME: Implement proper decimal sep alignment, e.g. via siunitx.
3238                                 if (decimal) {
3239                                         docstring const halffixedwith =
3240                                                 from_ascii(Length(column_info[c].p_width.value() / 2,
3241                                                                   column_info[c].p_width.unit()).asLatexString());
3242                                         os << '{'
3243                                            << halffixedwith
3244                                            << '}'
3245                                            << "@{\\extracolsep{0pt}" << column_info[c].decimal_point << "}"
3246                                            << valign
3247                                            << '{'
3248                                            << halffixedwith
3249                                            << '}';
3250                                 } else
3251                                         os << '{'
3252                                            << from_ascii(column_info[c].p_width.asLatexString())
3253                                            << '}';
3254                         } else if (column_info[c].varwidth) {
3255                                 switch (column_info[c].alignment) {
3256                                 case LYX_ALIGN_LEFT:
3257                                         os << ">{\\raggedright\\arraybackslash}";
3258                                         break;
3259                                 case LYX_ALIGN_RIGHT:
3260                                         os << ">{\\raggedleft\\arraybackslash}";
3261                                         break;
3262                                 case LYX_ALIGN_CENTER:
3263                                         os << ">{\\centering\\arraybackslash}";
3264                                         break;
3265                                 case LYX_ALIGN_NONE:
3266                                 case LYX_ALIGN_BLOCK:
3267                                 case LYX_ALIGN_LAYOUT:
3268                                 case LYX_ALIGN_SPECIAL:
3269                                 case LYX_ALIGN_DECIMAL:
3270                                         break;
3271                                 }
3272                                 os << 'X';
3273                         } else if (isVTypeColumn(c)) {
3274                                 switch (column_info[c].alignment) {
3275                                 case LYX_ALIGN_LEFT:
3276                                         os << ">{\\raggedright}";
3277                                         break;
3278                                 case LYX_ALIGN_RIGHT:
3279                                         os << ">{\\raggedleft}";
3280                                         break;
3281                                 case LYX_ALIGN_CENTER:
3282                                         os << ">{\\centering}";
3283                                         break;
3284                                 case LYX_ALIGN_NONE:
3285                                 case LYX_ALIGN_BLOCK:
3286                                 case LYX_ALIGN_LAYOUT:
3287                                 case LYX_ALIGN_SPECIAL:
3288                                 case LYX_ALIGN_DECIMAL:
3289                                         break;
3290                                 }
3291                                 os << "V{\\linewidth}";
3292                         } else {
3293                                 switch (column_info[c].alignment) {
3294                                 case LYX_ALIGN_LEFT:
3295                                         os << 'l';
3296                                         break;
3297                                 case LYX_ALIGN_RIGHT:
3298                                         os << 'r';
3299                                         break;
3300                                 case LYX_ALIGN_DECIMAL:
3301                                         os << "r@{\\extracolsep{0pt}" << column_info[c].decimal_point << "}l";
3302                                         break;
3303                                 default:
3304                                         os << 'c';
3305                                         break;
3306                                 }
3307                         } // end if else !column_info[i].p_width
3308                 } // end if else !column_info[i].align_special
3309                 if ((bidi_rtl && columnLeftLine(c)) || (!bidi_rtl && columnRightLine(c)))
3310                         os << '|';
3311         }
3312         os << "}\n";
3313
3314         TeXLongtableHeaderFooter(os, runparams, columns);
3315
3316         //+---------------------------------------------------------------------
3317         //+                      the single row and columns (cells)            +
3318         //+---------------------------------------------------------------------
3319
3320         for (row_type r = 0; r < nrows(); ++r) {
3321                 if (isValidRow(r)) {
3322                         TeXRow(os, r, runparams, columns);
3323                         if (is_long_tabular && row_info[r].newpage)
3324                                 os << "\\newpage\n";
3325                 }
3326         }
3327
3328         //+---------------------------------------------------------------------
3329         //+                      the closing of the tabular                    +
3330         //+---------------------------------------------------------------------
3331
3332         if (is_long_tabular) {
3333                 if (is_xltabular)
3334                         os << "\\end{xltabular}";
3335                 else
3336                         os << "\\end{longtable}";
3337         } else {
3338                 if (is_tabular_star)
3339                         os << "\\end{tabular*}";
3340                 else if (hasVarwidthColumn())
3341                         os << "\\end{tabularx}";
3342                 else
3343                         os << "\\end{tabular}";
3344         }
3345
3346         if (deactivate_chars)
3347                 // close the group
3348                 os << "\n\\endgroup\n";
3349
3350         if (rotate != 0) {
3351                 if (is_long_tabular)
3352                         os << breakln << "\\end{landscape}";
3353                 else
3354                         os << breakln << "\\end{turn}";
3355         }
3356
3357         if (!TexRow::isNone(pos))
3358                 os.texrow().start(pos);
3359 }
3360
3361
3362 int Tabular::docbookRow(odocstream & os, row_type row,
3363                            OutputParams const & runparams) const
3364 {
3365         int ret = 0;
3366         idx_type cell = getFirstCellInRow(row);
3367
3368         os << "<row>\n";
3369         for (col_type c = 0; c < ncols(); ++c) {
3370                 if (isPartOfMultiColumn(row, c))
3371                         continue;
3372
3373                 os << "<entry align=\"";
3374                 switch (getAlignment(cell)) {
3375                 case LYX_ALIGN_LEFT:
3376                         os << "left";
3377                         break;
3378                 case LYX_ALIGN_RIGHT:
3379                         os << "right";
3380                         break;
3381                 default:
3382                         os << "center";
3383                         break;
3384                 }
3385
3386                 os << "\" valign=\"";
3387                 switch (getVAlignment(cell)) {
3388                 case LYX_VALIGN_TOP:
3389                         os << "top";
3390                         break;
3391                 case LYX_VALIGN_BOTTOM:
3392                         os << "bottom";
3393                         break;
3394                 case LYX_VALIGN_MIDDLE:
3395                         os << "middle";
3396                 }
3397                 os << '"';
3398
3399                 if (isMultiColumn(cell)) {
3400                         os << " namest=\"col" << c << "\" ";
3401                         os << "nameend=\"col" << c + columnSpan(cell) - 1 << '"';
3402                 }
3403
3404                 os << '>';
3405                 ret += cellInset(cell)->docbook(os, runparams);
3406                 os << "</entry>\n";
3407                 ++cell;
3408         }
3409         os << "</row>\n";
3410         return ret;
3411 }
3412
3413
3414 int Tabular::docbook(odocstream & os, OutputParams const & runparams) const
3415 {
3416         int ret = 0;
3417
3418         //+---------------------------------------------------------------------
3419         //+                      first the opening preamble                    +
3420         //+---------------------------------------------------------------------
3421
3422         os << "<tgroup cols=\"" << ncols()
3423            << "\" colsep=\"1\" rowsep=\"1\">\n";
3424
3425         for (col_type c = 0; c < ncols(); ++c) {
3426                 os << "<colspec colname=\"col" << c << "\" align=\"";
3427                 switch (column_info[c].alignment) {
3428                 case LYX_ALIGN_LEFT:
3429                         os << "left";
3430                         break;
3431                 case LYX_ALIGN_RIGHT:
3432                         os << "right";
3433                         break;
3434                 default:
3435                         os << "center";
3436                         break;
3437                 }
3438                 os << '"';
3439                 if (runparams.flavor == OutputParams::XML)
3440                         os << '/';
3441                 os << ">\n";
3442                 ++ret;
3443         }
3444
3445         //+---------------------------------------------------------------------
3446         //+                      Long Tabular case                             +
3447         //+---------------------------------------------------------------------
3448
3449         // output caption info
3450         // The caption flag wins over head/foot
3451         if (haveLTCaption()) {
3452                 os << "<caption>\n";
3453                 ++ret;
3454                 for (row_type r = 0; r < nrows(); ++r) {
3455                         if (row_info[r].caption) {
3456                                 ret += docbookRow(os, r, runparams);
3457                         }
3458                 }
3459                 os << "</caption>\n";
3460                 ++ret;
3461         }
3462         // output header info
3463         if (haveLTHead(false) || haveLTFirstHead(false)) {
3464                 os << "<thead>\n";
3465                 ++ret;
3466                 for (row_type r = 0; r < nrows(); ++r) {
3467                         if ((row_info[r].endhead || row_info[r].endfirsthead) &&
3468                             !row_info[r].caption) {
3469                                 ret += docbookRow(os, r, runparams);
3470                         }
3471                 }
3472                 os << "</thead>\n";
3473                 ++ret;
3474         }
3475         // output footer info
3476         if (haveLTFoot(false) || haveLTLastFoot(false)) {
3477                 os << "<tfoot>\n";
3478                 ++ret;
3479                 for (row_type r = 0; r < nrows(); ++r) {
3480                         if ((row_info[r].endfoot || row_info[r].endlastfoot) &&
3481                             !row_info[r].caption) {
3482                                 ret += docbookRow(os, r, runparams);
3483                         }
3484                 }
3485                 os << "</tfoot>\n";
3486                 ++ret;
3487         }
3488
3489         //+---------------------------------------------------------------------
3490         //+                      the single row and columns (cells)            +
3491         //+---------------------------------------------------------------------
3492
3493         os << "<tbody>\n";
3494         ++ret;
3495         for (row_type r = 0; r < nrows(); ++r) {
3496                 if (isValidRow(r)) {
3497                         ret += docbookRow(os, r, runparams);
3498                 }
3499         }
3500         os << "</tbody>\n";
3501         ++ret;
3502         //+---------------------------------------------------------------------
3503         //+                      the closing of the tabular                    +
3504         //+---------------------------------------------------------------------
3505
3506         os << "</tgroup>";
3507         ++ret;
3508
3509         return ret;
3510 }
3511
3512
3513 docstring Tabular::xhtmlRow(XHTMLStream & xs, row_type row,
3514                            OutputParams const & runparams, bool header) const
3515 {
3516         docstring ret;
3517         string const celltag = header ? "th" : "td";
3518         idx_type cell = getFirstCellInRow(row);
3519
3520         xs << html::StartTag("tr");
3521         for (col_type c = 0; c < ncols(); ++c) {
3522                 if (isPartOfMultiColumn(row, c) || isPartOfMultiRow(row, c))
3523                         continue;
3524
3525                 stringstream attr;
3526
3527                 Length const cwidth = column_info[c].p_width;
3528                 if (!cwidth.zero()) {
3529                         string const hwidth = cwidth.asHTMLString();
3530                         attr << "style =\"width: " << hwidth << ";\" ";
3531                 }
3532
3533                 attr << "align='";
3534                 switch (getAlignment(cell)) {
3535                 case LYX_ALIGN_LEFT:
3536                         attr << "left";
3537                         break;
3538                 case LYX_ALIGN_RIGHT:
3539                         attr << "right";
3540                         break;
3541                 default:
3542                         attr << "center";
3543                         break;
3544                 }
3545                 attr << "'";
3546                 attr << " valign='";
3547                 switch (getVAlignment(cell)) {
3548                 case LYX_VALIGN_TOP:
3549                         attr << "top";
3550                         break;
3551                 case LYX_VALIGN_BOTTOM:
3552                         attr << "bottom";
3553                         break;
3554                 case LYX_VALIGN_MIDDLE:
3555                         attr << "middle";
3556                 }
3557                 attr << "'";
3558
3559                 if (isMultiColumn(cell))
3560                         attr << " colspan='" << columnSpan(cell) << "'";
3561                 else if (isMultiRow(cell))
3562                         attr << " rowspan='" << rowSpan(cell) << "'";
3563
3564                 xs << html::StartTag(celltag, attr.str(), true) << html::CR();
3565                 ret += cellInset(cell)->xhtml(xs, runparams);
3566                 xs << html::EndTag(celltag) << html::CR();
3567                 ++cell;
3568         }
3569         xs << html::EndTag("tr");
3570         return ret;
3571 }
3572
3573
3574 docstring Tabular::xhtml(XHTMLStream & xs, OutputParams const & runparams) const
3575 {
3576         docstring ret;
3577
3578         if (is_long_tabular) {
3579                 // we'll wrap it in a div, so as to deal with alignment
3580                 string align;
3581                 switch (longtabular_alignment) {
3582                 case LYX_LONGTABULAR_ALIGN_LEFT:
3583                         align = "left";
3584                         break;
3585                 case LYX_LONGTABULAR_ALIGN_CENTER:
3586                         align = "center";
3587                         break;
3588                 case LYX_LONGTABULAR_ALIGN_RIGHT:
3589                         align = "right";
3590                         break;
3591                 }
3592                 xs << html::StartTag("div", "class='longtable' style='text-align: " + align + ";'")
3593                    << html::CR();
3594                 // The caption flag wins over head/foot
3595                 if (haveLTCaption()) {
3596                         xs << html::StartTag("div", "class='longtable-caption' style='text-align: " + align + ";'")
3597                            << html::CR();
3598                         for (row_type r = 0; r < nrows(); ++r)
3599                                 if (row_info[r].caption)
3600                                         ret += xhtmlRow(xs, r, runparams);
3601                         xs << html::EndTag("div") << html::CR();
3602                 }
3603         }
3604
3605         xs << html::StartTag("table") << html::CR();
3606
3607         // output header info
3608         bool const havefirsthead = haveLTFirstHead(false);
3609         // if we have a first head, then we are going to ignore the
3610         // headers for the additional pages, since there aren't any
3611         // in XHTML. this test accomplishes that.
3612         bool const havehead = !havefirsthead && haveLTHead(false);
3613         if (havehead || havefirsthead) {
3614                 xs << html::StartTag("thead") << html::CR();
3615                 for (row_type r = 0; r < nrows(); ++r) {
3616                         if (((havefirsthead && row_info[r].endfirsthead) ||
3617                              (havehead && row_info[r].endhead)) &&
3618                             !row_info[r].caption) {
3619                                 ret += xhtmlRow(xs, r, runparams, true);
3620                         }
3621                 }
3622                 xs << html::EndTag("thead") << html::CR();
3623         }
3624         // output footer info
3625         bool const havelastfoot = haveLTLastFoot(false);
3626         // as before.
3627         bool const havefoot = !havelastfoot && haveLTFoot(false);
3628         if (havefoot || havelastfoot) {
3629                 xs << html::StartTag("tfoot") << html::CR();
3630                 for (row_type r = 0; r < nrows(); ++r) {
3631                         if (((havelastfoot && row_info[r].endlastfoot) ||
3632                              (havefoot && row_info[r].endfoot)) &&
3633                             !row_info[r].caption) {
3634                                 ret += xhtmlRow(xs, r, runparams);
3635                         }
3636                 }
3637                 xs << html::EndTag("tfoot") << html::CR();
3638         }
3639
3640         xs << html::StartTag("tbody") << html::CR();
3641         for (row_type r = 0; r < nrows(); ++r) {
3642                 if (isValidRow(r)) {
3643                         ret += xhtmlRow(xs, r, runparams);
3644                 }
3645         }
3646         xs << html::EndTag("tbody")
3647            << html::CR()
3648            << html::EndTag("table")
3649            << html::CR();
3650         if (is_long_tabular)
3651                 xs << html::EndTag("div") << html::CR();
3652         return ret;
3653 }
3654
3655
3656 bool Tabular::plaintextTopHLine(odocstringstream & os, row_type row,
3657                                    vector<unsigned int> const & clen) const
3658 {
3659         idx_type const fcell = getFirstCellInRow(row);
3660         idx_type const n = numberOfCellsInRow(row) + fcell;
3661         idx_type tmp = 0;
3662
3663         for (idx_type i = fcell; i < n; ++i) {
3664                 if (topLine(i)) {
3665                         ++tmp;
3666                         break;
3667                 }
3668         }
3669         if (!tmp)
3670                 return false;
3671
3672         char_type ch;
3673         for (idx_type i = fcell; i < n; ++i) {
3674                 if (topLine(i)) {
3675                         if (leftLine(i))
3676                                 os << "+-";
3677                         else
3678                                 os << "--";
3679                         ch = '-';
3680                 } else {
3681                         os << "  ";
3682                         ch = ' ';
3683                 }
3684                 col_type column = cellColumn(i);
3685                 int len = clen[column];
3686                 while (column < ncols() - 1
3687                        && isPartOfMultiColumn(row, ++column))
3688                         len += clen[column] + 4;
3689                 os << docstring(len, ch);
3690                 if (topLine(i)) {
3691                         if (rightLine(i))
3692                                 os << "-+";
3693                         else
3694                                 os << "--";
3695                 } else {
3696                         os << "  ";
3697                 }
3698         }
3699         os << endl;
3700         return true;
3701 }
3702
3703
3704 bool Tabular::plaintextBottomHLine(odocstringstream & os, row_type row,
3705                                       vector<unsigned int> const & clen) const
3706 {
3707         idx_type const fcell = getFirstCellInRow(row);
3708         idx_type const n = numberOfCellsInRow(row) + fcell;
3709         idx_type tmp = 0;
3710
3711         for (idx_type i = fcell; i < n; ++i) {
3712                 if (bottomLine(i)) {
3713                         ++tmp;
3714                         break;
3715                 }
3716         }
3717         if (!tmp)
3718                 return false;
3719
3720         char_type ch;
3721         for (idx_type i = fcell; i < n; ++i) {
3722                 if (bottomLine(i)) {
3723                         if (leftLine(i))
3724                                 os << "+-";
3725                         else
3726                                 os << "--";
3727                         ch = '-';
3728                 } else {
3729                         os << "  ";
3730                         ch = ' ';
3731                 }
3732                 col_type column = cellColumn(i);
3733                 int len = clen[column];
3734                 while (column < ncols() - 1 && isPartOfMultiColumn(row, ++column))
3735                         len += clen[column] + 4;
3736                 os << docstring(len, ch);
3737                 if (bottomLine(i)) {
3738                         if (rightLine(i))
3739                                 os << "-+";
3740                         else
3741                                 os << "--";
3742                 } else {
3743                         os << "  ";
3744                 }
3745         }
3746         os << endl;
3747         return true;
3748 }
3749
3750
3751 void Tabular::plaintextPrintCell(odocstringstream & os,
3752                                OutputParams const & runparams,
3753                                idx_type cell, row_type row, col_type column,
3754                                vector<unsigned int> const & clen,
3755                                bool onlydata, size_t max_length) const
3756 {
3757         odocstringstream sstr;
3758         cellInset(cell)->plaintext(sstr, runparams, max_length);
3759
3760         if (onlydata) {
3761                 os << sstr.str();
3762                 return;
3763         }
3764
3765         if (leftLine(cell))
3766                 os << "| ";
3767         else
3768                 os << "  ";
3769
3770         unsigned int len1 = sstr.str().length();
3771         unsigned int len2 = clen[column];
3772         while (column < ncols() - 1 && isPartOfMultiColumn(row, ++column))
3773                 len2 += clen[column] + 4;
3774         len2 -= len1;
3775
3776         switch (getAlignment(cell)) {
3777         default:
3778         case LYX_ALIGN_LEFT:
3779                 len1 = 0;
3780                 break;
3781         case LYX_ALIGN_RIGHT:
3782                 len1 = len2;
3783                 len2 = 0;
3784                 break;
3785         case LYX_ALIGN_CENTER:
3786                 len1 = len2 / 2;
3787                 len2 -= len1;
3788                 break;
3789         }
3790
3791         os << docstring(len1, ' ') << sstr.str()
3792            << docstring(len2, ' ');
3793
3794         if (rightLine(cell))
3795                 os << " |";
3796         else
3797                 os << "  ";
3798 }
3799
3800
3801 void Tabular::plaintext(odocstringstream & os,
3802                            OutputParams const & runparams, int const depth,
3803                            bool onlydata, char_type delim, size_t max_length) const
3804 {
3805         // first calculate the width of the single columns
3806         vector<unsigned int> clen(ncols());
3807
3808         if (!onlydata) {
3809                 // first all non multicolumn cells!
3810                 for (col_type c = 0; c < ncols(); ++c) {
3811                         clen[c] = 0;
3812                         for (row_type r = 0; r < nrows(); ++r) {
3813                                 idx_type cell = cellIndex(r, c);
3814                                 if (isMultiColumn(cell))
3815                                         continue;
3816                                 odocstringstream sstr;
3817                                 cellInset(cell)->plaintext(sstr, runparams, max_length);
3818                                 if (clen[c] < sstr.str().length())
3819                                         clen[c] = sstr.str().length();
3820                         }
3821                 }
3822                 // then all multicolumn cells!
3823                 for (col_type c = 0; c < ncols(); ++c) {
3824                         for (row_type r = 0; r < nrows(); ++r) {
3825                                 idx_type cell = cellIndex(r, c);
3826                                 if (cell_info[r][c].multicolumn != CELL_BEGIN_OF_MULTICOLUMN)
3827                                         continue;
3828                                 odocstringstream sstr;
3829                                 cellInset(cell)->plaintext(sstr, runparams, max_length);
3830                                 int len = int(sstr.str().length());
3831                                 idx_type const n = columnSpan(cell);
3832                                 for (col_type k = c; len > 0 && k < c + n - 1; ++k)
3833                                         len -= clen[k];
3834                                 if (len > int(clen[c + n - 1]))
3835                                         clen[c + n - 1] = len;
3836                         }
3837                 }
3838         }
3839         idx_type cell = 0;
3840         for (row_type r = 0; r < nrows(); ++r) {
3841                 if (!onlydata && plaintextTopHLine(os, r, clen))
3842                         os << docstring(depth * 2, ' ');
3843                 for (col_type c = 0; c < ncols(); ++c) {
3844                         if (isPartOfMultiColumn(r, c) || isPartOfMultiRow(r,c))
3845                                 continue;
3846                         if (onlydata && c > 0)
3847                                 // we don't use operator<< for single UCS4 character.
3848                                 // see explanation in docstream.h
3849                                 os.put(delim);
3850                         plaintextPrintCell(os, runparams, cell, r, c, clen, onlydata, max_length);
3851                         ++cell;
3852                         if (os.str().size() > max_length)
3853                                 break;
3854                 }
3855                 os << endl;
3856                 if (!onlydata) {
3857                         os << docstring(depth * 2, ' ');
3858                         if (plaintextBottomHLine(os, r, clen))
3859                                 os << docstring(depth * 2, ' ');
3860                 }
3861                 if (os.str().size() > max_length)
3862                         break;
3863         }
3864 }
3865
3866
3867 shared_ptr<InsetTableCell> Tabular::cellInset(idx_type cell)
3868 {
3869         return cell_info[cellRow(cell)][cellColumn(cell)].inset;
3870 }
3871
3872
3873 shared_ptr<InsetTableCell> Tabular::cellInset(row_type row, col_type column)
3874 {
3875         return cell_info[row][column].inset;
3876 }
3877
3878
3879 InsetTableCell const * Tabular::cellInset(idx_type cell) const
3880 {
3881         return cell_info[cellRow(cell)][cellColumn(cell)].inset.get();
3882 }
3883
3884
3885 void Tabular::setCellInset(row_type row, col_type column,
3886                            shared_ptr<InsetTableCell> ins)
3887 {
3888         CellData & cd = cell_info[row][column];
3889         cd.inset = ins;
3890 }
3891
3892
3893 void Tabular::validate(LaTeXFeatures & features) const
3894 {
3895         features.require("NeedTabularnewline");
3896         if (use_booktabs)
3897                 features.require("booktabs");
3898         if (is_long_tabular && !hasVarwidthColumn()) {
3899                 if (tabular_width.zero())
3900                         features.require("longtable");
3901                 else
3902                         features.require("xltabular");
3903         }
3904         if (rotate && is_long_tabular)
3905                 features.require("lscape");
3906         if (needRotating())
3907                 features.require("rotating");
3908         if (hasVarwidthColumn()) {
3909                 if (is_long_tabular)
3910                         features.require("xltabular");
3911                 else
3912                         features.require("tabularx");
3913         }
3914         for (idx_type cell = 0; cell < numberofcells; ++cell) {
3915                 if (isMultiRow(cell))
3916                         features.require("multirow");
3917                 if (getUsebox(cell) == BOX_VARWIDTH)
3918                         features.require("varwidth");
3919                 if (getVAlignment(cell) != LYX_VALIGN_TOP
3920                     || !getPWidth(cell).zero()
3921                     || isVTypeColumn(cellColumn(cell)))
3922                         features.require("array");
3923                 // Tell footnote that we need a savenote
3924                 // environment in non-long tables or
3925                 // longtable headers/footers
3926                 else if (!is_long_tabular && !features.inFloat())
3927                         features.saveNoteEnv("tabular");
3928                 else if (!isValidRow(cellRow(cell)))
3929                         features.saveNoteEnv("longtable");
3930
3931                 cellInset(cell)->validate(features);
3932                 features.saveNoteEnv(string());
3933         }
3934 }
3935
3936
3937 Tabular::BoxType Tabular::useBox(idx_type cell) const
3938 {
3939         ParagraphList const & parlist = cellInset(cell)->paragraphs();
3940         if (parlist.size() > 1)
3941                 return BOX_VARWIDTH;
3942
3943         ParagraphList::const_iterator cit = parlist.begin();
3944         ParagraphList::const_iterator end = parlist.end();
3945
3946         for (; cit != end; ++cit)
3947                 for (int i = 0; i < cit->size(); ++i)
3948                         if (cit->isNewline(i) || cit->layout().isEnvironment())
3949                                 return BOX_VARWIDTH;
3950
3951         return BOX_NONE;
3952 }
3953
3954
3955 /////////////////////////////////////////////////////////////////////
3956 //
3957 // InsetTableCell
3958 //
3959 /////////////////////////////////////////////////////////////////////
3960
3961 InsetTableCell::InsetTableCell(Buffer * buf)
3962         : InsetText(buf, InsetText::PlainLayout), isFixedWidth(false),
3963           isMultiColumn(false), isMultiRow(false), contentAlign(LYX_ALIGN_CENTER)
3964 {}
3965
3966
3967 bool InsetTableCell::forcePlainLayout(idx_type) const
3968 {
3969         return isMultiRow || (isMultiColumn && !isFixedWidth);
3970 }
3971
3972
3973 bool InsetTableCell::allowParagraphCustomization(idx_type) const
3974 {
3975         return isFixedWidth;
3976 }
3977
3978
3979 bool InsetTableCell::forceLocalFontSwitch() const
3980 {
3981         return true;
3982 }
3983
3984
3985 bool InsetTableCell::getStatus(Cursor & cur, FuncRequest const & cmd,
3986         FuncStatus & status) const
3987 {
3988         bool enabled = true;
3989         switch (cmd.action()) {
3990         case LFUN_INSET_DISSOLVE:
3991                 enabled = false;
3992                 break;
3993         case LFUN_MATH_DISPLAY:
3994                 if (!hasFixedWidth()) {
3995                         enabled = false;
3996                         break;
3997                 } //fall-through
3998         default:
3999                 return InsetText::getStatus(cur, cmd, status);
4000         }
4001         status.setEnabled(enabled);
4002         return true;
4003 }
4004
4005 docstring InsetTableCell::asString(bool intoInsets)
4006 {
4007         docstring retval;
4008         if (paragraphs().empty())
4009                 return retval;
4010         ParagraphList::const_iterator it = paragraphs().begin();
4011         ParagraphList::const_iterator en = paragraphs().end();
4012         bool first = true;
4013         for (; it != en; ++it) {
4014                 if (!first)
4015                         retval += "\n";
4016                 else
4017                         first = false;
4018                 retval += it->asString(intoInsets ? AS_STR_INSETS : AS_STR_NONE);
4019         }
4020         return retval;
4021 }
4022
4023
4024 void InsetTableCell::addToToc(DocIterator const & di, bool output_active,
4025                                                           UpdateType utype, TocBackend & backend) const
4026 {
4027         InsetText::iterateForToc(di, output_active, utype, backend);
4028 }
4029
4030
4031 docstring InsetTableCell::xhtml(XHTMLStream & xs, OutputParams const & rp) const
4032 {
4033         if (!isFixedWidth)
4034                 return InsetText::insetAsXHTML(xs, rp, InsetText::JustText);
4035         return InsetText::xhtml(xs, rp);
4036 }
4037
4038
4039 void InsetTableCell::metrics(MetricsInfo & mi, Dimension & dim) const
4040 {
4041         TextMetrics & tm = mi.base.bv->textMetrics(&text());
4042
4043         // Hand font through to contained lyxtext:
4044         tm.font_.fontInfo() = mi.base.font;
4045         mi.base.textwidth -= 2 * TEXT_TO_INSET_OFFSET;
4046
4047         // This can happen when a layout has a left and right margin,
4048         // and the view is made very narrow. We can't do better than
4049         // to draw it partly out of view (bug 5890).
4050         if (mi.base.textwidth < 1)
4051                 mi.base.textwidth = 1;
4052
4053         // We tell metrics here not to expand on multiple pars
4054         // This is the difference to InsetText::Metrics
4055         if (hasFixedWidth())
4056                 tm.metrics(mi, dim, mi.base.textwidth, false);
4057         else
4058                 tm.metrics(mi, dim, 0, false);
4059         mi.base.textwidth += 2 * TEXT_TO_INSET_OFFSET;
4060         dim.asc += TEXT_TO_INSET_OFFSET;
4061         dim.des += TEXT_TO_INSET_OFFSET;
4062         dim.wid += 2 * TEXT_TO_INSET_OFFSET;
4063 }
4064
4065
4066
4067 /////////////////////////////////////////////////////////////////////
4068 //
4069 // InsetTabular
4070 //
4071 /////////////////////////////////////////////////////////////////////
4072
4073 InsetTabular::InsetTabular(Buffer * buf, row_type rows,
4074                            col_type columns)
4075         : Inset(buf), tabular(buf, max(rows, row_type(1)), max(columns, col_type(1))),
4076           rowselect_(false), colselect_(false)
4077 {
4078 }
4079
4080
4081 InsetTabular::InsetTabular(InsetTabular const & tab)
4082         : Inset(tab), tabular(tab.tabular),
4083           rowselect_(false), colselect_(false)
4084 {
4085 }
4086
4087
4088 InsetTabular::~InsetTabular()
4089 {
4090         hideDialogs("tabular", this);
4091 }
4092
4093
4094 void InsetTabular::setBuffer(Buffer & buf)
4095 {
4096         tabular.setBuffer(buf);
4097         Inset::setBuffer(buf);
4098 }
4099
4100
4101 bool InsetTabular::insetAllowed(InsetCode code) const
4102 {
4103         switch (code) {
4104         case FLOAT_CODE:
4105         case MARGIN_CODE:
4106         case MATHMACRO_CODE:
4107         case WRAP_CODE:
4108                 return false;
4109
4110         case CAPTION_CODE:
4111                 return tabular.is_long_tabular;
4112
4113         default:
4114                 return true;
4115         }
4116 }
4117
4118
4119 bool InsetTabular::allowsCaptionVariation(std::string const & newtype) const
4120 {
4121         return tabular.is_long_tabular &&
4122                 (newtype == "Standard" || newtype == "Unnumbered");
4123 }
4124
4125
4126 void InsetTabular::write(ostream & os) const
4127 {
4128         os << "Tabular" << endl;
4129         tabular.write(os);
4130 }
4131
4132
4133 string InsetTabular::contextMenu(BufferView const &, int, int) const
4134 {
4135         // FIXME: depending on the selection state,
4136         // we could offer a different menu.
4137         return cell(0)->contextMenuName() + ";" + contextMenuName();
4138 }
4139
4140
4141 string InsetTabular::contextMenuName() const
4142 {
4143         return "context-tabular";
4144 }
4145
4146
4147 void InsetTabular::read(Lexer & lex)
4148 {
4149         //bool const old_format = (lex.getString() == "\\LyXTable");
4150
4151         tabular.read(lex);
4152
4153         //if (old_format)
4154         //      return;
4155
4156         lex.next();
4157         string token = lex.getString();
4158         while (lex && token != "\\end_inset") {
4159                 lex.next();
4160                 token = lex.getString();
4161         }
4162         if (!lex)
4163                 lex.printError("Missing \\end_inset at this point. ");
4164 }
4165
4166
4167 int InsetTabular::rowFromY(Cursor & cur, int y) const
4168 {
4169         // top y coordinate of tabular
4170         int h = yo(cur.bv()) - tabular.rowAscent(0) + tabular.offsetVAlignment();
4171         row_type r = 0;
4172         for (; r < tabular.nrows() && y > h; ++r)
4173                 h += tabular.rowAscent(r) + tabular.rowDescent(r)
4174                         + tabular.interRowSpace(r);
4175
4176         return r - 1;
4177 }
4178
4179
4180 int InsetTabular::columnFromX(Cursor & cur, int x) const
4181 {
4182         // left x coordinate of tabular
4183         int w = xo(cur.bv()) + ADD_TO_TABULAR_WIDTH;
4184         col_type c = 0;
4185         for (; c < tabular.ncols() && x > w; ++c)
4186                 w += tabular.cellWidth(c);
4187         return c - 1;
4188 }
4189
4190
4191 void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
4192 {
4193         //lyxerr << "InsetTabular::metrics: " << mi.base.bv << " width: " <<
4194         //      mi.base.textwidth << "\n";
4195         LBUFERR(mi.base.bv);
4196
4197         for (row_type r = 0; r < tabular.nrows(); ++r) {
4198                 int maxasc = 0;
4199                 int maxdes = 0;
4200                 for (col_type c = 0; c < tabular.ncols(); ++c) {
4201                         if (tabular.isPartOfMultiColumn(r, c)
4202                                 || tabular.isPartOfMultiRow(r, c))
4203                                 // multicolumn or multirow cell, but not first one
4204                                 continue;
4205                         idx_type const cell = tabular.cellIndex(r, c);
4206                         Dimension dim0;
4207                         MetricsInfo m = mi;
4208                         Length const p_width = tabular.getPWidth(cell);
4209                         if (!p_width.zero())
4210                                 m.base.textwidth = mi.base.inPixels(p_width);
4211                         else if (tabular.column_info[c].varwidth)
4212                                 m.base.textwidth = tabular.column_info[c].width;
4213                         tabular.cellInset(cell)->metrics(m, dim0);
4214                         if (!p_width.zero() || tabular.column_info[c].varwidth)
4215                                 dim0.wid = m.base.textwidth;
4216                         tabular.cellInfo(cell).width = dim0.wid + 2 * WIDTH_OF_LINE
4217                                 + tabular.interColumnSpace(cell);
4218
4219                         // FIXME(?): do we need a second metrics call?
4220                         TextMetrics const & tm =
4221                                 mi.base.bv->textMetrics(tabular.cellInset(cell)->getText(0));
4222
4223                         // determine horizontal offset because of decimal align (if necessary)
4224                         int decimal_width = 0;
4225                         if (tabular.getAlignment(cell) == LYX_ALIGN_DECIMAL) {
4226                                 InsetTableCell tail = InsetTableCell(*tabular.cellInset(cell));
4227                                 tail.setBuffer(tabular.buffer());
4228                                 // we need to set macrocontext position everywhere
4229                                 // otherwise we crash with nested insets (e.g. footnotes)
4230                                 // after decimal point
4231                                 DocIterator dit = tabular.cellInset(cell)->getText(0)->macrocontextPosition();
4232                                 dit.pop_back();
4233                                 dit.push_back(CursorSlice(tail));
4234                                 tail.setMacrocontextPositionRecursive(dit);
4235
4236                                 // remove text leading decimal point
4237                                 docstring const align_d = tabular.column_info[c].decimal_point;
4238                                 dit = separatorPos(&tail, align_d);
4239
4240                                 pit_type const psize = tail.paragraphs().front().size();
4241                                 if (dit) {
4242                                         tail.paragraphs().front().eraseChars(0,
4243                                                 dit.pos() < psize ? dit.pos() + 1 : psize, false);
4244                                         Dimension dim1;
4245                                         tail.metrics(m, dim1);
4246                                         decimal_width = dim1.width();
4247                                 }
4248                         }
4249                         tabular.cell_info[r][c].decimal_hoffset = tm.width() - decimal_width;
4250                         tabular.cell_info[r][c].decimal_width = decimal_width;
4251
4252                         // with LYX_VALIGN_BOTTOM the descent is relative to the last par
4253                         // = descent of text in last par + TEXT_TO_INSET_OFFSET:
4254                         int const lastpardes = tm.last().second->descent()
4255                                 + TEXT_TO_INSET_OFFSET;
4256                         int offset = 0;
4257                         switch (tabular.getVAlignment(cell)) {
4258                                 case Tabular::LYX_VALIGN_TOP:
4259                                         break;
4260                                 case Tabular::LYX_VALIGN_MIDDLE:
4261                                         offset = -(dim0.des - lastpardes)/2;
4262                                         break;
4263                                 case Tabular::LYX_VALIGN_BOTTOM:
4264                                         offset = -(dim0.des - lastpardes);
4265                                         break;
4266                         }
4267                         tabular.cell_info[r][c].voffset = offset;
4268                         maxasc = max(maxasc, dim0.asc - offset);
4269                         maxdes = max(maxdes, dim0.des + offset);
4270                 }
4271                 int const top_space = tabular.row_info[r].top_space_default ?
4272                     default_line_space :
4273                     mi.base.inPixels(tabular.row_info[r].top_space);
4274                 tabular.setRowAscent(r, maxasc + ADD_TO_HEIGHT + top_space);
4275                 int const bottom_space = tabular.row_info[r].bottom_space_default ?
4276                     default_line_space :
4277                     mi.base.inPixels(tabular.row_info[r].bottom_space);
4278                 tabular.setRowDescent(r, maxdes + ADD_TO_HEIGHT + bottom_space);
4279         }
4280
4281         // We need to recalculate the metrics after column width calculation
4282         // with xtabular (possibly multiple times, so the call is recursive).
4283         if (tabular.updateColumnWidths(mi) && tabular.hasVarwidthColumn())
4284                 metrics(mi, dim);
4285         dim.asc = tabular.rowAscent(0) - tabular.offsetVAlignment();
4286         dim.des = tabular.height() - dim.asc;
4287         dim.wid = tabular.width() + 2 * ADD_TO_TABULAR_WIDTH;
4288 }
4289
4290
4291 bool InsetTabular::isCellSelected(Cursor & cur, row_type row, col_type col) const
4292 {
4293         if (&cur.inset() == this && cur.selection()) {
4294                 if (cur.selIsMultiCell()) {
4295                         row_type rs, re;
4296                         col_type cs, ce;
4297                         getSelection(cur, rs, re, cs, ce);
4298
4299                         idx_type const cell = tabular.cellIndex(row, col);
4300                         col_type const cspan = tabular.columnSpan(cell);
4301                         row_type const rspan = tabular.rowSpan(cell);
4302                         if (col + cspan - 1 >= cs && col <= ce
4303                                 && row + rspan - 1 >= rs && row <= re)
4304                                 return true;
4305                 } else
4306                         if (col == tabular.cellColumn(cur.idx())
4307                                 && row == tabular.cellRow(cur.idx())) {
4308                         CursorSlice const & beg = cur.selBegin();
4309                         CursorSlice const & end = cur.selEnd();
4310
4311                         if ((end.lastpos() > 0 || end.lastpit() > 0)
4312                                   && end.pos() == end.lastpos() && beg.pos() == 0
4313                                   && end.pit() == end.lastpit() && beg.pit() == 0)
4314                                 return true;
4315                 }
4316         }
4317         return false;
4318 }
4319
4320
4321 void InsetTabular::draw(PainterInfo & pi, int x, int y) const
4322 {
4323         x += ADD_TO_TABULAR_WIDTH;
4324
4325         BufferView * bv = pi.base.bv;
4326         Cursor & cur = pi.base.bv->cursor();
4327
4328         // FIXME: As the full background is painted in drawBackground(),
4329         // we have no choice but to do a full repaint for the Text cells.
4330         pi.full_repaint = true;
4331
4332         bool const original_selection_state = pi.selected;
4333
4334         idx_type idx = 0;
4335
4336         int yy = y + tabular.offsetVAlignment();
4337         for (row_type r = 0; r < tabular.nrows(); ++r) {
4338                 int nx = x;
4339                 for (col_type c = 0; c < tabular.ncols(); ++c) {
4340                         if (tabular.isPartOfMultiColumn(r, c))
4341                                 continue;
4342
4343                         idx = tabular.cellIndex(r, c);
4344
4345                         if (tabular.isPartOfMultiRow(r, c)) {
4346                                 nx += tabular.cellWidth(idx);
4347                                 continue;
4348                         }
4349
4350                         pi.selected |= isCellSelected(cur, r, c);
4351                         int const cx = nx + tabular.textHOffset(idx);
4352                         int const cy = yy + tabular.textVOffset(idx);
4353                         // Cache the Inset position.
4354                         bv->coordCache().insets().add(cell(idx).get(), cx, cy);
4355                         cell(idx)->draw(pi, cx, cy);
4356                         drawCellLines(pi, nx, yy, r, idx);
4357                         nx += tabular.cellWidth(idx);
4358                         pi.selected = original_selection_state;
4359                 }
4360
4361                 if (r + 1 < tabular.nrows())
4362                         yy += tabular.rowDescent(r) + tabular.rowAscent(r + 1)
4363                                 + tabular.interRowSpace(r + 1);
4364         }
4365 }
4366
4367
4368 void InsetTabular::drawBackground(PainterInfo & pi, int x, int y) const
4369 {
4370         x += ADD_TO_TABULAR_WIDTH;
4371         y += tabular.offsetVAlignment() - tabular.rowAscent(0);
4372         pi.pain.fillRectangle(x, y, tabular.width(), tabular.height(),
4373                 pi.backgroundColor(this));
4374 }
4375
4376
4377 void InsetTabular::drawSelection(PainterInfo & pi, int x, int y) const
4378 {
4379         Cursor & cur = pi.base.bv->cursor();
4380
4381         x += ADD_TO_TABULAR_WIDTH;
4382
4383         if (!cur.selection())
4384                 return;
4385         if (&cur.inset() != this)
4386                 return;
4387
4388         //resetPos(cur);
4389
4390         bool const full_cell_selected = isCellSelected(cur,
4391                 tabular.cellRow(cur.idx()), tabular.cellColumn(cur.idx()));
4392
4393         if (cur.selIsMultiCell() || full_cell_selected) {
4394                 for (row_type r = 0; r < tabular.nrows(); ++r) {
4395                         int xx = x;
4396                         for (col_type c = 0; c < tabular.ncols(); ++c) {
4397                                 if (tabular.isPartOfMultiColumn(r, c))
4398                                         continue;
4399
4400                                 idx_type const cell = tabular.cellIndex(r, c);
4401
4402                                 if (tabular.isPartOfMultiRow(r, c)) {
4403                                         xx += tabular.cellWidth(cell);
4404                                         continue;
4405                                 }
4406                                 int const w = tabular.cellWidth(cell);
4407                                 int const h = tabular.cellHeight(cell);
4408                                 int const yy = y - tabular.rowAscent(r) + tabular.offsetVAlignment();
4409                                 if (isCellSelected(cur, r, c))
4410                                         pi.pain.fillRectangle(xx, yy, w, h, Color_selection);
4411                                 xx += w;
4412                         }
4413                         if (r + 1 < tabular.nrows())
4414                                 y += tabular.rowDescent(r) + tabular.rowAscent(r + 1)
4415                                      + tabular.interRowSpace(r + 1);
4416                 }
4417
4418         }
4419         // FIXME: This code has no effect because InsetTableCell does not handle
4420         // drawSelection other than the trivial implementation in Inset.
4421         //else {
4422         //      x += cellXPos(cur.idx());
4423         //      x += tabular.textHOffset(cur.idx());
4424         //      cell(cur.idx())->drawSelection(pi, x, 0 /* ignored */);
4425         //}
4426 }
4427
4428
4429 namespace {
4430
4431 void tabline(PainterInfo const & pi, int x1, int y1, int x2, int y2, int lt, int rt,
4432              bool drawline, bool heavy = false)
4433 {
4434         ColorCode const col = drawline ? Color_tabularline : Color_tabularonoffline;
4435         if (drawline && lt > 0)
4436                 pi.pain.line(x1, y1, x1 + lt, y2, pi.textColor(Color_tabularonoffline),
4437                                          Painter::line_onoffdash,
4438                                          Painter::thin_line);
4439         pi.pain.line(x1 + lt, y1, x2 - rt, y2, pi.textColor(col),
4440                                  drawline ? Painter::line_solid : Painter::line_onoffdash,
4441                                  (heavy ? 2 : 1) * Painter::thin_line);
4442         if (drawline && rt > 0)
4443                 pi.pain.line(x2 - rt, y1, x2, y2, pi.textColor(Color_tabularonoffline),
4444                                          Painter::line_onoffdash,
4445                                          Painter::thin_line);
4446 }
4447
4448 }
4449
4450
4451 void InsetTabular::drawCellLines(PainterInfo & pi, int x, int y,
4452                                  row_type row, idx_type cell) const
4453 {
4454         y -= tabular.rowAscent(row);
4455         int const w = tabular.cellWidth(cell);
4456         int const h = tabular.cellHeight(cell);
4457         int lt = 0;
4458         int rt = 0;
4459
4460         col_type const col = tabular.cellColumn(cell);
4461
4462         // Top
4463         bool drawline = tabular.topLine(cell)
4464                 || (row > 0 && tabular.bottomLine(tabular.cellAbove(cell)));
4465         bool heavy = tabular.use_booktabs
4466                         && (row == 0 || (tabular.is_long_tabular && row == 1 && tabular.ltCaption(0)))
4467                         && tabular.rowTopLine(row);
4468         if (tabular.topLineTrim(cell).first
4469             || (row > 0 && tabular.bottomLineTrim(tabular.cellIndex(row - 1, col)).first))
4470                 lt = 10;
4471         if (tabular.topLineTrim(cell).second
4472             || (row > 0 && tabular.bottomLineTrim(tabular.cellIndex(row - 1, col)).second))
4473                 rt = 10;
4474         tabline(pi, x, y, x + w, y, lt, rt, drawline, heavy);
4475
4476         // Bottom
4477         lt = rt = 0;
4478         drawline = tabular.bottomLine(cell);
4479         row_type const lastrow = tabular.nrows() - 1;
4480         // Consider multi-rows
4481         row_type r = row;
4482         while (r < lastrow && tabular.isMultiRow(tabular.cellIndex(r, col))
4483                 && tabular.isPartOfMultiRow(r + 1, col))
4484                 r++;
4485         heavy = tabular.use_booktabs
4486                 && ((row == lastrow && tabular.rowBottomLine(row))
4487                     || (r == lastrow && tabular.rowBottomLine(r)));
4488         if (tabular.bottomLineTrim(cell).first)
4489                 lt = 10;
4490         if (tabular.bottomLineTrim(cell).second)
4491                 rt = 10;
4492         tabline(pi, x, y + h, x + w, y + h, lt, rt, drawline, heavy);
4493
4494         // Left
4495         drawline = tabular.leftLine(cell)
4496                 || (col > 0 && tabular.rightLine(tabular.cellIndex(row, col - 1)));
4497         tabline(pi, x, y, x, y + h, 0, 0, drawline);
4498
4499         // Right
4500         x -= tabular.interColumnSpace(cell);
4501         col_type next_cell_col = col + 1;
4502         while (next_cell_col < tabular.ncols()
4503                 && tabular.isMultiColumn(tabular.cellIndex(row, next_cell_col)))
4504                 next_cell_col++;
4505         drawline = tabular.rightLine(cell)
4506                    || (next_cell_col < tabular.ncols()
4507                        && tabular.leftLine(tabular.cellIndex(row, next_cell_col)));
4508         tabline(pi, x + w, y, x + w, y + h, 0, 0, drawline);
4509 }
4510
4511
4512 void InsetTabular::edit(Cursor & cur, bool front, EntryDirection)
4513 {
4514         //lyxerr << "InsetTabular::edit: " << this << endl;
4515         cur.finishUndo();
4516         cur.push(*this);
4517         if (front) {
4518                 if (isRightToLeft(cur))
4519                         cur.idx() = tabular.getLastCellInRow(0);
4520                 else
4521                         cur.idx() = 0;
4522                 cur.pit() = 0;
4523                 cur.pos() = 0;
4524         } else {
4525                 if (isRightToLeft(cur))
4526                         cur.idx() = tabular.getFirstCellInRow(tabular.nrows() - 1);
4527                 else
4528                         cur.idx() = tabular.numberofcells - 1;
4529                 cur.pit() = 0;
4530                 cur.pos() = cur.lastpos(); // FIXME crude guess
4531         }
4532         cur.setCurrentFont();
4533         // FIXME: this accesses the position cache before it is initialized
4534         //cur.bv().fitCursor();
4535 }
4536
4537
4538 void InsetTabular::updateBuffer(ParIterator const & it, UpdateType utype)
4539 {
4540         // In a longtable, tell captions what the current float is
4541         Counters & cnts = buffer().masterBuffer()->params().documentClass().counters();
4542         string const saveflt = cnts.current_float();
4543         if (tabular.is_long_tabular) {
4544                 cnts.current_float("table");
4545                 // in longtables, we only step the counter once
4546                 cnts.step(from_ascii("table"), utype);
4547                 cnts.isLongtable(true);
4548         }
4549
4550         ParIterator it2 = it;
4551         it2.forwardPos();
4552         size_t const end = it2.nargs();
4553         for ( ; it2.idx() < end; it2.top().forwardIdx())
4554                 buffer().updateBuffer(it2, utype);
4555
4556         //reset afterwards
4557         if (tabular.is_long_tabular) {
4558                 cnts.current_float(saveflt);
4559                 cnts.isLongtable(false);
4560         }
4561 }
4562
4563
4564 void InsetTabular::addToToc(DocIterator const & cpit, bool output_active,
4565                                                         UpdateType utype, TocBackend & backend) const
4566 {
4567         DocIterator dit = cpit;
4568         dit.forwardPos();
4569         size_t const end = dit.nargs();
4570         for ( ; dit.idx() < end; dit.top().forwardIdx())
4571                 cell(dit.idx())->addToToc(dit, output_active, utype, backend);
4572 }
4573
4574
4575 bool InsetTabular::hitSelectRow(BufferView const & bv, int x) const
4576 {
4577         int const x0 = xo(bv) + ADD_TO_TABULAR_WIDTH;
4578         return x < x0 || x > x0 + tabular.width();
4579 }
4580
4581
4582 bool InsetTabular::hitSelectColumn(BufferView const & bv, int y) const
4583 {
4584         int const y0 = yo(bv) - tabular.rowAscent(0) + tabular.offsetVAlignment();
4585         // FIXME: using ADD_TO_TABULAR_WIDTH is not really correct since
4586         // there is no margin added vertically to tabular insets.
4587         // However, it works for now.
4588         return y < y0 + ADD_TO_TABULAR_WIDTH || y > y0 + tabular.height() - ADD_TO_TABULAR_WIDTH;
4589 }
4590
4591
4592 bool InsetTabular::clickable(BufferView const & bv, int x, int y) const
4593 {
4594         return hitSelectRow(bv, x) || hitSelectColumn(bv, y);
4595 }
4596
4597
4598 void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
4599 {
4600         LYXERR(Debug::DEBUG, "# InsetTabular::doDispatch: cmd: " << cmd
4601                              << "\n  cur:" << cur);
4602         CursorSlice sl = cur.top();
4603         Cursor & bvcur = cur.bv().cursor();
4604
4605         FuncCode const act = cmd.action();
4606
4607         switch (act) {
4608
4609         case LFUN_MOUSE_PRESS: {
4610                 //lyxerr << "# InsetTabular::MousePress\n" << cur.bv().cursor() << endl;
4611                 // select row
4612                 if (hitSelectRow(cur.bv(), cmd.x())) {
4613                         row_type r = rowFromY(cur, cmd.y());
4614                         cur.idx() = tabular.getFirstCellInRow(r);
4615                         cur.pit() = 0;
4616                         cur.pos() = 0;
4617                         cur.resetAnchor();
4618                         cur.idx() = tabular.getLastCellInRow(r);
4619                         cur.pit() = cur.lastpit();
4620                         cur.pos() = cur.lastpos();
4621                         cur.selection(true);
4622                         bvcur = cur;
4623                         rowselect_ = true;
4624                         break;
4625                 }
4626                 // select column
4627                 if (hitSelectColumn(cur.bv(), cmd.y())) {
4628                         col_type c = columnFromX(cur, cmd.x());
4629                         cur.idx() = tabular.cellIndex(0, c);
4630                         cur.pit() = 0;
4631                         cur.pos() = 0;
4632                         cur.resetAnchor();
4633                         cur.idx() = tabular.cellIndex(tabular.nrows() - 1, c);
4634                         cur.pit() = cur.lastpit();
4635                         cur.pos() = cur.lastpos();
4636                         cur.selection(true);
4637                         bvcur = cur;
4638                         colselect_ = true;
4639                         break;
4640                 }
4641                 // do not reset cursor/selection if we have selected
4642                 // some cells (bug 2715).
4643                 if (cmd.button() == mouse_button::button3
4644                     && &bvcur.selBegin().inset() == this
4645                     && bvcur.selIsMultiCell())
4646                         ;
4647                 else
4648                         // Let InsetTableCell do it
4649                         cell(cur.idx())->dispatch(cur, cmd);
4650                 break;
4651         }
4652         case LFUN_MOUSE_MOTION:
4653                 //lyxerr << "# InsetTabular::MouseMotion\n" << bvcur << endl;
4654                 if (cmd.button() == mouse_button::button1) {
4655                         // only accept motions to places not deeper nested than the real anchor
4656                         if (!bvcur.realAnchor().hasPart(cur)) {
4657                                 cur.undispatched();
4658                                 break;
4659                         }
4660                         // select (additional) row
4661                         if (rowselect_) {
4662                                 row_type r = rowFromY(cur, cmd.y());
4663                                 cur.idx() = tabular.getLastCellInRow(r);
4664                                 // we need to reset the cursor's pit and pos now, as the old ones
4665                                 // may no longer be valid.
4666                                 cur.pit() = 0;
4667                                 cur.pos() = 0;
4668                                 bvcur.setCursor(cur);
4669                                 bvcur.selection(true);
4670                                 break;
4671                         }
4672                         // select (additional) column
4673                         if (colselect_) {
4674                                 col_type c = columnFromX(cur, cmd.x());
4675                                 cur.idx() = tabular.cellIndex(tabular.nrows() - 1, c);
4676                                 // we need to reset the cursor's pit and pos now, as the old ones
4677                                 // may no longer be valid.
4678                                 cur.pit() = 0;
4679                                 cur.pos() = 0;
4680                                 bvcur.setCursor(cur);
4681                                 bvcur.selection(true);
4682                                 break;
4683                         }
4684                         // only update if selection changes
4685                         if (bvcur.idx() == cur.idx() &&
4686                                 !(bvcur.realAnchor().idx() == cur.idx() && bvcur.pos() != cur.pos()))
4687                                 cur.noScreenUpdate();
4688                         setCursorFromCoordinates(cur, cmd.x(), cmd.y());
4689                         bvcur.setCursor(cur);
4690                         bvcur.selection(true);
4691                         // if this is a multicell selection, we just set the cursor to
4692                         // the beginning of the cell's text.
4693                         if (bvcur.selIsMultiCell()) {
4694                                 bvcur.pit() = bvcur.lastpit();
4695                                 bvcur.pos() = bvcur.lastpos();
4696                         }
4697                 }
4698                 break;
4699
4700         case LFUN_MOUSE_RELEASE:
4701                 rowselect_ = false;
4702                 colselect_ = false;
4703                 break;
4704
4705         case LFUN_CELL_BACKWARD:
4706                 movePrevCell(cur);
4707                 cur.selection(false);
4708                 break;
4709
4710         case LFUN_CELL_FORWARD:
4711                 moveNextCell(cur);
4712                 cur.selection(false);
4713                 break;
4714
4715         case LFUN_CHAR_FORWARD_SELECT:
4716         case LFUN_CHAR_FORWARD:
4717         case LFUN_CHAR_BACKWARD_SELECT:
4718         case LFUN_CHAR_BACKWARD:
4719         case LFUN_CHAR_RIGHT_SELECT:
4720         case LFUN_CHAR_RIGHT:
4721         case LFUN_CHAR_LEFT_SELECT:
4722         case LFUN_CHAR_LEFT:
4723         case LFUN_WORD_FORWARD:
4724         case LFUN_WORD_FORWARD_SELECT:
4725         case LFUN_WORD_BACKWARD:
4726         case LFUN_WORD_BACKWARD_SELECT:
4727         case LFUN_WORD_RIGHT:
4728         case LFUN_WORD_RIGHT_SELECT:
4729         case LFUN_WORD_LEFT:
4730         case LFUN_WORD_LEFT_SELECT: {
4731                 // determine whether we move to next or previous cell, where to enter
4732                 // the new cell from, and which command to "finish" (i.e., exit the
4733                 // inset) with:
4734                 bool next_cell;
4735                 EntryDirection entry_from = ENTRY_DIRECTION_IGNORE;
4736                 FuncCode finish_lfun;
4737
4738                 if (act == LFUN_CHAR_FORWARD
4739                                 || act == LFUN_CHAR_FORWARD_SELECT
4740                                 || act == LFUN_WORD_FORWARD
4741                                 || act == LFUN_WORD_FORWARD_SELECT) {
4742                         next_cell = true;
4743                         finish_lfun = LFUN_FINISHED_FORWARD;
4744                 }
4745                 else if (act == LFUN_CHAR_BACKWARD
4746                                 || act == LFUN_CHAR_BACKWARD_SELECT
4747                                 || act == LFUN_WORD_BACKWARD
4748                                 || act == LFUN_WORD_BACKWARD_SELECT) {
4749                         next_cell = false;
4750                         finish_lfun = LFUN_FINISHED_BACKWARD;
4751                 }
4752                 // LEFT or RIGHT commands --- the interpretation will depend on the
4753                 // table's direction.
4754                 else {
4755                         bool const right = act == LFUN_CHAR_RIGHT
4756                                 || act == LFUN_CHAR_RIGHT_SELECT
4757                                 || act == LFUN_WORD_RIGHT
4758                                 || act == LFUN_WORD_RIGHT_SELECT;
4759                         next_cell = isRightToLeft(cur) != right;
4760
4761                         if (lyxrc.visual_cursor)
4762                                 entry_from = right ? ENTRY_DIRECTION_LEFT:ENTRY_DIRECTION_RIGHT;
4763
4764                         finish_lfun = right ? LFUN_FINISHED_RIGHT : LFUN_FINISHED_LEFT;
4765                 }
4766
4767                 bool const select =     act == LFUN_CHAR_FORWARD_SELECT
4768                     || act == LFUN_CHAR_BACKWARD_SELECT
4769                     || act == LFUN_CHAR_RIGHT_SELECT
4770                     || act == LFUN_CHAR_LEFT_SELECT
4771                         || act == LFUN_WORD_FORWARD_SELECT
4772                         || act == LFUN_WORD_RIGHT_SELECT
4773                         || act == LFUN_WORD_BACKWARD_SELECT
4774                         || act == LFUN_WORD_LEFT_SELECT;
4775
4776                 // If we have a multicell selection or we're
4777                 // not doing some LFUN_*_SELECT thing anyway...
4778                 if (!cur.selIsMultiCell() || !select) {
4779                         col_type const c = tabular.cellColumn(cur.idx());
4780                         row_type const r = tabular.cellRow(cur.idx());
4781                         // Are we trying to select the whole cell and is the whole cell
4782                         // not yet selected?
4783                         bool const select_whole = select && !isCellSelected(cur, r, c) &&
4784                                 ((next_cell && cur.pit() == cur.lastpit()
4785                                 && cur.pos() == cur.lastpos())
4786                                 || (!next_cell && cur.pit() == 0 && cur.pos() == 0));
4787
4788                         bool const empty_cell = cur.lastpos() == 0 && cur.lastpit() == 0;
4789
4790                         // ...try to dispatch to the cell's inset.
4791                         cell(cur.idx())->dispatch(cur, cmd);
4792
4793                         // When we already have a selection we want to select the whole cell
4794                         // before going to the next cell.
4795                         if (select_whole && !empty_cell){
4796                                 getText(cur.idx())->selectAll(cur);
4797                                 cur.dispatched();
4798                                 cur.screenUpdateFlags(Update::Force | Update::FitCursor);
4799                                 break;
4800                         }
4801
4802                         // FIXME: When we support the selection of an empty cell, remove
4803                         // the !empty_cell from this condition. For now we jump to the next
4804                         // cell if the current cell is empty.
4805                         if (cur.result().dispatched() && !empty_cell)
4806                                 break;
4807                 }
4808
4809                 // move to next/prev cell, as appropriate
4810                 // note that we will always do this if we're selecting and we have
4811                 // a multicell selection
4812                 LYXERR(Debug::RTL, "entering " << (next_cell ? "next" : "previous")
4813                         << " cell from: " << int(entry_from));
4814                 if (next_cell)
4815                         moveNextCell(cur, entry_from);
4816                 else
4817                         movePrevCell(cur, entry_from);
4818                 // if we're exiting the table, call the appropriate FINISHED lfun
4819                 if (sl == cur.top()) {
4820                         cmd = FuncRequest(finish_lfun);
4821                         cur.undispatched();
4822                 } else
4823                         cur.dispatched();
4824
4825                 cur.screenUpdateFlags(Update::Force | Update::FitCursor);
4826                 break;
4827
4828         }
4829
4830         case LFUN_DOWN_SELECT:
4831         case LFUN_DOWN:
4832                 if (!(cur.selection() && cur.selIsMultiCell()))
4833                         cell(cur.idx())->dispatch(cur, cmd);
4834
4835                 cur.dispatched(); // override the cell's decision
4836                 if (sl == cur.top()) {
4837                         // if our Text didn't do anything to the cursor
4838                         // then we try to put the cursor into the cell below
4839                         // setting also the right targetX.
4840                         cur.selHandle(act == LFUN_DOWN_SELECT);
4841                         if (tabular.cellRow(cur.idx()) != tabular.nrows() - 1) {
4842                                 int const xtarget = cur.targetX();
4843                                 // WARNING: Once cur.idx() has been reset, the cursor is in
4844                                 // an inconsistent state until pos() has been set. Be careful
4845                                 // what you do with it!
4846                                 cur.idx() = tabular.cellBelow(cur.idx());
4847                                 cur.pit() = 0;
4848                                 TextMetrics const & tm =
4849                                         cur.bv().textMetrics(cell(cur.idx())->getText(0));
4850                                 cur.pos() = tm.x2pos(cur.pit(), 0, xtarget);
4851                                 cur.setCurrentFont();
4852                         }
4853                 }
4854                 if (sl == cur.top()) {
4855                         // we trick it to go to forward after leaving the
4856                         // tabular.
4857                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
4858                         cur.undispatched();
4859                 }
4860                 if (cur.selIsMultiCell()) {
4861                         cur.pit() = cur.lastpit();
4862                         cur.pos() = cur.lastpos();
4863                         cur.setCurrentFont();
4864                         cur.screenUpdateFlags(Update::Force | Update::FitCursor);
4865                         return;
4866                 }
4867                 cur.screenUpdateFlags(Update::Force | Update::FitCursor);
4868                 break;
4869
4870         case LFUN_UP_SELECT:
4871         case LFUN_UP:
4872                 if (!(cur.selection() && cur.selIsMultiCell()))
4873                         cell(cur.idx())->dispatch(cur, cmd);
4874                 cur.dispatched(); // override the cell's decision
4875                 if (sl == cur.top()) {
4876                         // if our Text didn't do anything to the cursor
4877                         // then we try to put the cursor into the cell above
4878                         // setting also the right targetX.
4879                         cur.selHandle(act == LFUN_UP_SELECT);
4880                         if (tabular.cellRow(cur.idx()) != 0) {
4881                                 int const xtarget = cur.targetX();
4882                                 // WARNING: Once cur.idx() has been reset, the cursor is in
4883                                 // an inconsistent state until pos() has been set. Be careful
4884                                 // what you do with it!
4885                                 cur.idx() = tabular.cellAbove(cur.idx());
4886                                 cur.pit() = cur.lastpit();
4887                                 Text const * text = cell(cur.idx())->getText(0);
4888                                 TextMetrics const & tm = cur.bv().textMetrics(text);
4889                                 ParagraphMetrics const & pm =
4890                                         tm.parMetrics(cur.lastpit());
4891                                 cur.pos() = tm.x2pos(cur.pit(), pm.rows().size()-1, xtarget);
4892                                 cur.setCurrentFont();
4893                         }
4894                 }
4895                 if (sl == cur.top()) {
4896                         cmd = FuncRequest(LFUN_UP);
4897                         cur.undispatched();
4898                 }
4899                 if (cur.selIsMultiCell()) {
4900                         cur.pit() = 0;
4901                         cur.pos() = cur.lastpos();
4902                         cur.setCurrentFont();
4903                         cur.screenUpdateFlags(Update::Force | Update::FitCursor);
4904                         return;
4905                 }
4906                 cur.screenUpdateFlags(Update::Force | Update::FitCursor);
4907                 break;
4908
4909         case LFUN_LAYOUT_TABULAR:
4910                 cur.bv().showDialog("tabular");
4911                 break;
4912
4913         case LFUN_INSET_MODIFY:
4914                 // we come from the dialog
4915                 if (cmd.getArg(0) == "tabular")
4916                         tabularFeatures(cur, cmd.getLongArg(1));
4917                 else
4918                         cur.undispatched();
4919                 break;
4920
4921         case LFUN_TABULAR_FEATURE:
4922                 tabularFeatures(cur, to_utf8(cmd.argument()));
4923                 break;
4924
4925         // insert file functions
4926         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
4927         case LFUN_FILE_INSERT_PLAINTEXT:
4928                 // FIXME UNICODE
4929                 if (FileName::isAbsolute(to_utf8(cmd.argument()))) {
4930                         docstring const tmpstr = cur.bv().contentsOfPlaintextFile(
4931                                 FileName(to_utf8(cmd.argument())));
4932                         if (tmpstr.empty())
4933                                 break;
4934                         cur.recordUndoInset();
4935                         if (insertPlaintextString(cur.bv(), tmpstr, false)) {
4936                                 // content has been replaced,
4937                                 // so cursor might be invalid
4938                                 cur.pos() = cur.lastpos();
4939                                 cur.pit() = cur.lastpit();
4940                                 bvcur.setCursor(cur);
4941                         } else
4942                                 cur.undispatched();
4943                 }
4944                 break;
4945
4946         case LFUN_CUT:
4947                 if (cur.selIsMultiCell()) {
4948                         if (copySelection(cur)) {
4949                                 cur.recordUndoInset();
4950                                 cutSelection(cur);
4951                         }
4952                 } else
4953                         cell(cur.idx())->dispatch(cur, cmd);
4954                 break;
4955
4956         case LFUN_SELF_INSERT:
4957                 if (cur.selIsMultiCell()) {
4958                         cur.recordUndoInset();
4959                         cutSelection(cur);
4960                         BufferView * bv = &cur.bv();
4961                         docstring::const_iterator cit = cmd.argument().begin();
4962                         docstring::const_iterator const end = cmd.argument().end();
4963                         for (; cit != end; ++cit)
4964                                 bv->translateAndInsert(*cit, getText(cur.idx()), cur);
4965
4966                         cur.resetAnchor();
4967                         bv->bookmarkEditPosition();
4968                 } else
4969                         cell(cur.idx())->dispatch(cur, cmd);
4970                 break;
4971
4972         case LFUN_CHAR_DELETE_BACKWARD:
4973         case LFUN_CHAR_DELETE_FORWARD:
4974                 if (cur.selIsMultiCell()) {
4975                         cur.recordUndoInset();
4976                         cutSelection(cur);
4977                 } else
4978                         cell(cur.idx())->dispatch(cur, cmd);
4979                 break;
4980
4981         case LFUN_COPY:
4982                 if (!cur.selection())
4983                         break;
4984                 if (cur.selIsMultiCell()) {
4985                         cur.finishUndo();
4986                         copySelection(cur);
4987                 } else
4988                         cell(cur.idx())->dispatch(cur, cmd);
4989                 break;
4990
4991         case LFUN_CLIPBOARD_PASTE:
4992         case LFUN_PRIMARY_SELECTION_PASTE: {
4993                 docstring const clip = (act == LFUN_CLIPBOARD_PASTE) ?
4994                         theClipboard().getAsText(Clipboard::PlainTextType) :
4995                         theSelection().get();
4996                 if (clip.empty())
4997                         break;
4998                 // pass to InsertPlaintextString, but
4999                 // only if we have multi-cell content
5000                 if (clip.find_first_of(from_ascii("\t\n")) != docstring::npos) {
5001                         cur.recordUndoInset();
5002                         if (insertPlaintextString(cur.bv(), clip, false)) {
5003                                 // content has been replaced,
5004                                 // so cursor might be invalid
5005                                 cur.pos() = cur.lastpos();
5006                                 cur.pit() = cur.lastpit();
5007                                 bvcur.setCursor(cur);
5008                                 break;
5009                         }
5010                 }
5011                 // Let the cell handle normal text
5012                 cell(cur.idx())->dispatch(cur, cmd);
5013                 break;
5014         }
5015
5016         case LFUN_PASTE:
5017                 if (!tabularStackDirty()) {
5018                         // Check if we have plain text or HTML with rows/columns.
5019                         // and if so, pass over to LFUN_CLIPBOARD_PASTE
5020                         if (theClipboard().hasTextContents(Clipboard::AnyTextType)
5021                             && !theClipboard().hasTextContents(Clipboard::LyXTextType)) {
5022                                 docstring const clip =
5023                                         theClipboard().getAsText(Clipboard::AnyTextType);
5024                                 if (clip.find_first_of(from_ascii("\t\n")) != docstring::npos) {
5025                                         FuncRequest ncmd = FuncRequest(LFUN_CLIPBOARD_PASTE, cmd.argument());
5026                                         doDispatch(cur, ncmd);
5027                                         break;
5028                                 }
5029                         }
5030                         if (!cur.selIsMultiCell())
5031                                 cell(cur.idx())->dispatch(cur, cmd);
5032                         break;
5033                 }
5034                 if (theClipboard().isInternal()) {
5035                         cur.recordUndoInset();
5036                         pasteClipboard(cur);
5037                 }
5038                 break;
5039
5040         case LFUN_CHANGE_ACCEPT:
5041         case LFUN_CHANGE_REJECT:
5042         case LFUN_FONT_EMPH:
5043         case LFUN_FONT_BOLD:
5044         case LFUN_FONT_BOLDSYMBOL:
5045         case LFUN_FONT_ROMAN:
5046         case LFUN_FONT_NOUN:
5047         case LFUN_FONT_ITAL:
5048         case LFUN_FONT_FRAK:
5049         case LFUN_FONT_TYPEWRITER:
5050         case LFUN_FONT_SANS:
5051         case LFUN_TEXTSTYLE_APPLY:
5052         case LFUN_TEXTSTYLE_UPDATE:
5053         case LFUN_FONT_SIZE:
5054         case LFUN_FONT_UNDERLINE:
5055         case LFUN_FONT_STRIKEOUT:
5056         case LFUN_FONT_CROSSOUT:
5057         case LFUN_FONT_UNDERUNDERLINE:
5058         case LFUN_FONT_UNDERWAVE:
5059         case LFUN_LANGUAGE:
5060         case LFUN_PARAGRAPH_PARAMS_APPLY:
5061         case LFUN_PARAGRAPH_PARAMS:
5062         case LFUN_WORD_CAPITALIZE:
5063         case LFUN_WORD_UPCASE:
5064         case LFUN_WORD_LOWCASE:
5065         case LFUN_CHARS_TRANSPOSE:
5066                 if (cur.selIsMultiCell()) {
5067                         row_type rs, re;
5068                         col_type cs, ce;
5069                         getSelection(cur, rs, re, cs, ce);
5070                         Cursor tmpcur = cur;
5071                         for (row_type r = rs; r <= re; ++r) {
5072                                 for (col_type c = cs; c <= ce; ++c) {
5073                                         // cursor follows cell:
5074                                         tmpcur.idx() = tabular.cellIndex(r, c);
5075                                         // select this cell only:
5076                                         tmpcur.pit() = 0;
5077                                         tmpcur.pos() = 0;
5078                                         tmpcur.resetAnchor();
5079                                         tmpcur.pit() = tmpcur.lastpit();
5080                                         tmpcur.pos() = tmpcur.top().lastpos();
5081                                         tmpcur.setCursor(tmpcur);
5082                                         tmpcur.setSelection();
5083                                         cell(tmpcur.idx())->dispatch(tmpcur, cmd);
5084                                 }
5085                         }
5086                         if (act == LFUN_CHANGE_ACCEPT || act == LFUN_CHANGE_REJECT) {
5087                                 // cursor might be invalid
5088                                 cur.fixIfBroken();
5089                         }
5090                         break;
5091                 } else {
5092                         cell(cur.idx())->dispatch(cur, cmd);
5093                         break;
5094                 }
5095
5096         case LFUN_INSET_SETTINGS:
5097                 // relay this lfun to Inset, not to the cell.
5098                 Inset::doDispatch(cur, cmd);
5099                 break;
5100
5101         default:
5102                 // we try to handle this event in the insets dispatch function.
5103                 cell(cur.idx())->dispatch(cur, cmd);
5104                 break;
5105         }
5106 }
5107
5108
5109 bool InsetTabular::getFeatureStatus(Cursor & cur, string const & s,
5110                       string const & argument, FuncStatus & status) const
5111 {
5112
5113                 int action = Tabular::LAST_ACTION;
5114                 int i = 0;
5115                 for (; tabularFeature[i].action != Tabular::LAST_ACTION; ++i) {
5116                         if (tabularFeature[i].feature == s) {
5117                                 action = tabularFeature[i].action;
5118                                 break;
5119                         }
5120                 }
5121                 if (action == Tabular::LAST_ACTION) {
5122                         status.clear();
5123                         status.setUnknown(true);
5124                         return true;
5125                 }
5126
5127                 row_type sel_row_start = 0;
5128                 row_type sel_row_end = 0;
5129                 col_type sel_col_start = 0;
5130                 col_type sel_col_end = 0;
5131                 Tabular::ltType dummyltt;
5132                 bool flag = true;
5133
5134                 getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
5135
5136                 switch (action) {
5137                 case Tabular::SET_PWIDTH:
5138                 case Tabular::SET_MPWIDTH:
5139                 case Tabular::TOGGLE_VARWIDTH_COLUMN:
5140                 case Tabular::SET_SPECIAL_COLUMN:
5141                 case Tabular::SET_SPECIAL_MULTICOLUMN:
5142                 case Tabular::APPEND_ROW:
5143                 case Tabular::APPEND_COLUMN:
5144                 case Tabular::DELETE_ROW:
5145                 case Tabular::DELETE_COLUMN:
5146                 case Tabular::COPY_ROW:
5147                 case Tabular::COPY_COLUMN:
5148                 case Tabular::SET_TOP_SPACE:
5149                 case Tabular::SET_BOTTOM_SPACE:
5150                 case Tabular::SET_INTERLINE_SPACE:
5151                         status.clear();
5152                         return true;
5153
5154                 case Tabular::SET_TABULAR_WIDTH:
5155                         status.setEnabled(!tabular.rotate
5156                                 && tabular.tabular_valignment == Tabular::LYX_VALIGN_MIDDLE);
5157                         break;
5158
5159                 case Tabular::MOVE_COLUMN_RIGHT:
5160                 case Tabular::MOVE_COLUMN_LEFT:
5161                 case Tabular::MOVE_ROW_DOWN:
5162                 case Tabular::MOVE_ROW_UP: {
5163                         if (cur.selection()) {
5164                                 status.message(_("Selections not supported."));
5165                                 status.setEnabled(false);
5166                                 break;
5167                         }
5168
5169                         if ((action == Tabular::MOVE_COLUMN_RIGHT &&
5170                                 tabular.ncols() == tabular.cellColumn(cur.idx()) + 1) ||
5171                             (action == Tabular::MOVE_COLUMN_LEFT &&
5172                                 tabular.cellColumn(cur.idx()) == 0) ||
5173                             (action == Tabular::MOVE_ROW_DOWN &&
5174                                 tabular.nrows() == tabular.cellRow(cur.idx()) + 1) ||
5175                             (action == Tabular::MOVE_ROW_UP &&
5176                                 tabular.cellRow(cur.idx()) == 0)) {
5177                                         status.setEnabled(false);
5178                                         break;
5179                         }
5180
5181                         if (action == Tabular::MOVE_COLUMN_RIGHT ||
5182                             action == Tabular::MOVE_COLUMN_LEFT) {
5183                                 if (tabular.hasMultiColumn(tabular.cellColumn(cur.idx())) ||
5184                                     tabular.hasMultiColumn(tabular.cellColumn(cur.idx()) +
5185                                         (action == Tabular::MOVE_COLUMN_RIGHT ? 1 : -1))) {
5186                                         status.message(_("Multi-column in current or"
5187                                                          " destination column."));
5188                                         status.setEnabled(false);
5189                                         break;
5190                                 }
5191                         }
5192
5193                         if (action == Tabular::MOVE_ROW_DOWN ||
5194                             action == Tabular::MOVE_ROW_UP) {
5195                                 if (tabular.hasMultiRow(tabular.cellRow(cur.idx())) ||
5196                                     tabular.hasMultiRow(tabular.cellRow(cur.idx()) +
5197                                         (action == Tabular::MOVE_ROW_DOWN ? 1 : -1))) {
5198                                         status.message(_("Multi-row in current or"
5199                                                          " destination row."));
5200                                         status.setEnabled(false);
5201                                         break;
5202                                 }
5203                         }
5204
5205                         status.setEnabled(true);
5206                         break;
5207                 }
5208
5209                 case Tabular::SET_DECIMAL_POINT:
5210                         status.setEnabled(
5211                                 tabular.getAlignment(cur.idx()) == LYX_ALIGN_DECIMAL);
5212                         break;
5213
5214                 case Tabular::SET_MULTICOLUMN:
5215                 case Tabular::UNSET_MULTICOLUMN:
5216                 case Tabular::MULTICOLUMN:
5217                         // If a row is set as longtable caption, it must not be allowed
5218                         // to unset that this row is a multicolumn.
5219                         status.setEnabled(sel_row_start == sel_row_end
5220                                 && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5221                         status.setOnOff(tabular.isMultiColumn(cur.idx()));
5222                         break;
5223
5224                 case Tabular::SET_MULTIROW:
5225                 case Tabular::UNSET_MULTIROW:
5226                 case Tabular::MULTIROW:
5227                         // If a row is set as longtable caption, it must not be allowed
5228                         // to unset that this row is a multirow.
5229                         status.setEnabled(sel_col_start == sel_col_end
5230                                 && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5231                         status.setOnOff(tabular.isMultiRow(cur.idx()));
5232                         break;
5233
5234                 case Tabular::SET_ALL_LINES:
5235                 case Tabular::UNSET_ALL_LINES:
5236                 case Tabular::SET_INNER_LINES:
5237                 case Tabular::SET_BORDER_LINES:
5238                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
5239                         break;
5240
5241                 case Tabular::RESET_FORMAL_DEFAULT:
5242                         status.setEnabled(tabular.use_booktabs);
5243                         break;
5244
5245                 case Tabular::SET_LINE_TOP:
5246                 case Tabular::SET_LINE_BOTTOM:
5247                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
5248                         break;
5249
5250                 case Tabular::SET_LINE_LEFT:
5251                 case Tabular::SET_LINE_RIGHT:
5252                         status.setEnabled(!tabular.use_booktabs
5253                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5254                         break;
5255
5256                 case Tabular::SET_LTRIM_TOP:
5257                 case Tabular::SET_RTRIM_TOP:
5258                         status.setEnabled(tabular.use_booktabs
5259                                           && tabular.cellRow(cur.idx()) != 0
5260                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5261                         break;
5262
5263                 case Tabular::SET_LTRIM_BOTTOM:
5264                 case Tabular::SET_RTRIM_BOTTOM:
5265                         status.setEnabled(tabular.use_booktabs
5266                                           && tabular.cellRow(cur.idx()) != tabular.nrows() - 1
5267                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5268                         break;
5269
5270                 case Tabular::TOGGLE_LINE_TOP:
5271                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
5272                         status.setOnOff(tabular.topLine(cur.idx()));
5273                         break;
5274
5275                 case Tabular::TOGGLE_LINE_BOTTOM:
5276                         status.setEnabled(!tabular.ltCaption(tabular.cellRow(cur.idx())));
5277                         status.setOnOff(tabular.bottomLine(cur.idx()));
5278                         break;
5279
5280                 case Tabular::TOGGLE_LINE_LEFT:
5281                         status.setEnabled(!tabular.use_booktabs
5282                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5283                         status.setOnOff(tabular.leftLine(cur.idx()));
5284                         break;
5285
5286                 case Tabular::TOGGLE_LINE_RIGHT:
5287                         status.setEnabled(!tabular.use_booktabs
5288                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5289                         status.setOnOff(tabular.rightLine(cur.idx()));
5290                         break;
5291
5292                 case Tabular::TOGGLE_LTRIM_TOP:
5293                         status.setEnabled(tabular.use_booktabs
5294                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5295                         status.setOnOff(tabular.topLineTrim(cur.idx()).first);
5296                         break;
5297
5298                 case Tabular::TOGGLE_RTRIM_TOP:
5299                         status.setEnabled(tabular.use_booktabs
5300                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5301                         status.setOnOff(tabular.topLineTrim(cur.idx()).second);
5302                         break;
5303
5304                 case Tabular::TOGGLE_LTRIM_BOTTOM:
5305                         status.setEnabled(tabular.use_booktabs
5306                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5307                         status.setOnOff(tabular.bottomLineTrim(cur.idx()).first);
5308                         break;
5309
5310                 case Tabular::TOGGLE_RTRIM_BOTTOM:
5311                         status.setEnabled(tabular.use_booktabs
5312                                           && !tabular.ltCaption(tabular.cellRow(cur.idx())));
5313                         status.setOnOff(tabular.bottomLineTrim(cur.idx()).second);
5314                         break;
5315
5316                 // multirow cells only inherit the alignment of the column if the column has
5317                 // no width, otherwise they are left-aligned
5318                 // therefore allow always left but right and center only if there is no width
5319                 case Tabular::M_ALIGN_LEFT:
5320                         flag = false;
5321                         // fall through
5322                 case Tabular::ALIGN_LEFT:
5323                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_LEFT);
5324                         break;
5325
5326                 case Tabular::M_ALIGN_RIGHT:
5327                         flag = false;
5328                         // fall through
5329                 case Tabular::ALIGN_RIGHT:
5330                         status.setEnabled(!(tabular.isMultiRow(cur.idx())
5331                                 && !tabular.getPWidth(cur.idx()).zero()));
5332                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_RIGHT);
5333                         break;
5334
5335                 case Tabular::M_ALIGN_CENTER:
5336                         flag = false;
5337                         // fall through
5338                 case Tabular::ALIGN_CENTER:
5339                         status.setEnabled(!(tabular.isMultiRow(cur.idx())
5340                                 && !tabular.getPWidth(cur.idx()).zero()));
5341                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_CENTER);
5342                         break;
5343
5344                 case Tabular::ALIGN_BLOCK:
5345                         status.setEnabled(!tabular.getPWidth(cur.idx()).zero()
5346                                 && !tabular.isMultiRow(cur.idx()));
5347                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_BLOCK);
5348                         break;
5349
5350                 case Tabular::ALIGN_DECIMAL:
5351                         status.setEnabled(!tabular.isMultiRow(cur.idx())
5352                                 && !tabular.isMultiColumn(cur.idx()));
5353                         status.setOnOff(tabular.getAlignment(cur.idx(), true) == LYX_ALIGN_DECIMAL);
5354                         break;
5355
5356                 case Tabular::M_VALIGN_TOP:
5357                         flag = false;
5358                         // fall through
5359                 case Tabular::VALIGN_TOP:
5360                         status.setEnabled(!tabular.getPWidth(cur.idx()).zero()
5361                                 && !tabular.isMultiRow(cur.idx()));
5362                         status.setOnOff(
5363                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_TOP);
5364                         break;
5365
5366                 case Tabular::M_VALIGN_BOTTOM:
5367                         flag = false;
5368                         // fall through
5369                 case Tabular::VALIGN_BOTTOM:
5370                         status.setEnabled(!tabular.getPWidth(cur.idx()).zero()
5371                                 && !tabular.isMultiRow(cur.idx()));
5372                         status.setOnOff(
5373                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_BOTTOM);
5374                         break;
5375
5376                 case Tabular::M_VALIGN_MIDDLE:
5377                         flag = false;
5378                         // fall through
5379                 case Tabular::VALIGN_MIDDLE:
5380                         status.setEnabled(!tabular.getPWidth(cur.idx()).zero()
5381                                 && !tabular.isMultiRow(cur.idx()));
5382                         status.setOnOff(
5383                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_MIDDLE);
5384                         break;
5385
5386                 case Tabular::SET_LONGTABULAR:
5387                 case Tabular::TOGGLE_LONGTABULAR:
5388                         // setting as longtable is not allowed when table is inside a float
5389                         if (cur.innerInsetOfType(FLOAT_CODE) != 0
5390                                 || cur.innerInsetOfType(WRAP_CODE) != 0)
5391                                 status.setEnabled(false);
5392                         else
5393                                 status.setEnabled(true);
5394                         status.setOnOff(tabular.is_long_tabular);
5395                         break;
5396
5397                 case Tabular::UNSET_LONGTABULAR:
5398                         status.setOnOff(!tabular.is_long_tabular);
5399                         break;
5400
5401                 case Tabular::TOGGLE_ROTATE_TABULAR:
5402                 case Tabular::SET_ROTATE_TABULAR:
5403                         status.setOnOff(tabular.rotate != 0);
5404                         break;
5405
5406                 case Tabular::TABULAR_VALIGN_TOP:
5407                         status.setEnabled(tabular.tabular_width.zero());
5408                         status.setOnOff(tabular.tabular_valignment
5409                                 == Tabular::LYX_VALIGN_TOP);
5410                         break;
5411                 case Tabular::TABULAR_VALIGN_MIDDLE:
5412                         status.setEnabled(tabular.tabular_width.zero());
5413                         status.setOnOff(tabular.tabular_valignment
5414                                 == Tabular::LYX_VALIGN_MIDDLE);
5415                         break;
5416                 case Tabular::TABULAR_VALIGN_BOTTOM:
5417                         status.setEnabled(tabular.tabular_width.zero());
5418                         status.setOnOff(tabular.tabular_valignment
5419                                 == Tabular::LYX_VALIGN_BOTTOM);
5420                         break;
5421
5422                 case Tabular::LONGTABULAR_ALIGN_LEFT:
5423                         status.setOnOff(tabular.longtabular_alignment
5424                                 == Tabular::LYX_LONGTABULAR_ALIGN_LEFT);
5425                         break;
5426                 case Tabular::LONGTABULAR_ALIGN_CENTER:
5427                         status.setOnOff(tabular.longtabular_alignment
5428                                 == Tabular::LYX_LONGTABULAR_ALIGN_CENTER);
5429                         break;
5430                 case Tabular::LONGTABULAR_ALIGN_RIGHT:
5431                         status.setOnOff(tabular.longtabular_alignment
5432                                 == Tabular::LYX_LONGTABULAR_ALIGN_RIGHT);
5433                         break;
5434
5435                 case Tabular::UNSET_ROTATE_TABULAR:
5436                         status.setOnOff(tabular.rotate == 0);
5437                         break;
5438
5439                 case Tabular::TOGGLE_ROTATE_CELL:
5440                 case Tabular::SET_ROTATE_CELL:
5441                         status.setOnOff(!oneCellHasRotationState(false,
5442                                 sel_row_start, sel_row_end, sel_col_start, sel_col_end));
5443                         break;
5444
5445                 case Tabular::UNSET_ROTATE_CELL:
5446                         status.setOnOff(!oneCellHasRotationState(true,
5447                                 sel_row_start, sel_row_end, sel_col_start, sel_col_end));
5448                         break;
5449
5450                 case Tabular::SET_USEBOX:
5451                         status.setOnOff(convert<int>(argument) == tabular.getUsebox(cur.idx()));
5452                         break;
5453
5454                 // every row can only be one thing:
5455                 // either a footer or header
5456                 case Tabular::SET_LTFIRSTHEAD:
5457                         status.setEnabled(sel_row_start == sel_row_end);
5458                         status.setOnOff(tabular.getRowOfLTFirstHead(sel_row_start, dummyltt));
5459                         break;
5460
5461                 case Tabular::UNSET_LTFIRSTHEAD:
5462                         status.setEnabled(sel_row_start == sel_row_end);
5463                         status.setOnOff(!tabular.getRowOfLTFirstHead(sel_row_start, dummyltt));
5464                         break;
5465
5466                 case Tabular::SET_LTHEAD:
5467                         status.setEnabled(sel_row_start == sel_row_end);
5468                         status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
5469                         break;
5470
5471                 case Tabular::UNSET_LTHEAD:
5472                         status.setEnabled(sel_row_start == sel_row_end);
5473                         status.setOnOff(!tabular.getRowOfLTHead(sel_row_start, dummyltt));
5474                         break;
5475
5476                 case Tabular::SET_LTFOOT:
5477                         status.setEnabled(sel_row_start == sel_row_end);
5478                         status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
5479                         break;
5480
5481                 case Tabular::UNSET_LTFOOT:
5482                         status.setEnabled(sel_row_start == sel_row_end);
5483                         status.setOnOff(!tabular.getRowOfLTFoot(sel_row_start, dummyltt));
5484                         break;
5485
5486                 case Tabular::SET_LTLASTFOOT:
5487                         status.setEnabled(sel_row_start == sel_row_end);
5488                         status.setOnOff(tabular.getRowOfLTLastFoot(sel_row_start, dummyltt));
5489                         break;
5490
5491                 case Tabular::UNSET_LTLASTFOOT:
5492                         status.setEnabled(sel_row_start == sel_row_end);
5493                         status.setOnOff(!tabular.getRowOfLTLastFoot(sel_row_start, dummyltt));
5494                         break;
5495
5496                 case Tabular::SET_LTNEWPAGE:
5497                         status.setOnOff(tabular.getLTNewPage(sel_row_start));
5498                         break;
5499                 case Tabular::UNSET_LTNEWPAGE:
5500                         status.setOnOff(!tabular.getLTNewPage(sel_row_start));
5501                         break;
5502
5503                 // only one row in head/firsthead/foot/lasthead can be the caption
5504                 // and a multirow cannot be set as caption
5505                 case Tabular::SET_LTCAPTION:
5506                         status.setEnabled(sel_row_start == sel_row_end
5507                                 && (!tabular.getRowOfLTFirstHead(sel_row_start, dummyltt)
5508                                  || !tabular.haveLTCaption(Tabular::CAPTION_FIRSTHEAD))
5509                                 && (!tabular.getRowOfLTHead(sel_row_start, dummyltt)
5510                                  || !tabular.haveLTCaption(Tabular::CAPTION_HEAD))
5511                                 && (!tabular.getRowOfLTFoot(sel_row_start, dummyltt)
5512                                  || !tabular.haveLTCaption(Tabular::CAPTION_FOOT))
5513                                 && (!tabular.getRowOfLTLastFoot(sel_row_start, dummyltt)
5514                                  || !tabular.haveLTCaption(Tabular::CAPTION_LASTFOOT))
5515                                 && !tabular.isMultiRow(sel_row_start));
5516                         status.setOnOff(tabular.ltCaption(sel_row_start));
5517                         break;
5518
5519                 case Tabular::UNSET_LTCAPTION:
5520                         status.setEnabled(sel_row_start == sel_row_end && tabular.ltCaption(sel_row_start));
5521                         break;
5522
5523                 case Tabular::TOGGLE_LTCAPTION:
5524                         status.setEnabled(sel_row_start == sel_row_end && (tabular.ltCaption(sel_row_start)
5525                                 || ((!tabular.getRowOfLTFirstHead(sel_row_start, dummyltt)
5526                                   || !tabular.haveLTCaption(Tabular::CAPTION_FIRSTHEAD))
5527                                  && (!tabular.getRowOfLTHead(sel_row_start, dummyltt)
5528                                   || !tabular.haveLTCaption(Tabular::CAPTION_HEAD))
5529                                  && (!tabular.getRowOfLTFoot(sel_row_start, dummyltt)
5530                                   || !tabular.haveLTCaption(Tabular::CAPTION_FOOT))
5531                                  && (!tabular.getRowOfLTLastFoot(sel_row_start, dummyltt)
5532                                   || !tabular.haveLTCaption(Tabular::CAPTION_LASTFOOT)))));
5533                         status.setOnOff(tabular.ltCaption(sel_row_start));
5534                         break;
5535
5536                 case Tabular::TOGGLE_BOOKTABS:
5537                 case Tabular::SET_BOOKTABS:
5538                         status.setOnOff(tabular.use_booktabs);
5539                         break;
5540
5541                 case Tabular::UNSET_BOOKTABS:
5542                         status.setOnOff(!tabular.use_booktabs);
5543                         break;
5544
5545                 default:
5546                         status.clear();
5547                         status.setEnabled(false);
5548                         break;
5549                 }
5550                 return true;
5551 }
5552
5553
5554 // function sets an object as defined in FuncStatus.h:
5555 // states OK, Unknown, Disabled, On, Off.
5556 bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
5557                              FuncStatus & status) const
5558 {
5559         switch (cmd.action()) {
5560         case LFUN_INSET_MODIFY:
5561                 if (cmd.getArg(0) != "tabular")
5562                         break;
5563                 if (cmd.getArg(1) == "for-dialog") {
5564                         // The dialog is asking the status of a command
5565                         if (&cur.inset() != this)
5566                                 break;
5567                         string action = cmd.getArg(2);
5568                         string arg = cmd.getLongArg(3);
5569                         return getFeatureStatus(cur, action, arg, status);
5570                 } else {
5571                         // We always enable the lfun if it is coming from the dialog
5572                         // because the dialog makes sure all the settings are valid,
5573                         // even though the first argument might not be valid now.
5574                         status.setEnabled(true);
5575                         return true;
5576                 }
5577
5578         case LFUN_TABULAR_FEATURE: {
5579                 if (&cur.inset() != this)
5580                         break;
5581                 string action = cmd.getArg(0);
5582                 string arg = cmd.getLongArg(1);
5583                 return getFeatureStatus(cur, action, arg, status);
5584         }
5585
5586         case LFUN_CAPTION_INSERT: {
5587                 // caption is only allowed in caption cell of longtable
5588                 if (!tabular.ltCaption(tabular.cellRow(cur.idx()))) {
5589                         status.setEnabled(false);
5590                         return true;
5591                 }
5592                 // only standard caption is allowed
5593                 string arg = cmd.getArg(0);
5594                 if (!arg.empty() && arg != "Standard") {
5595                         status.setEnabled(false);
5596                         return true;
5597                 }
5598                 // check if there is already a caption
5599                 bool have_caption = false;
5600                 InsetTableCell itc = InsetTableCell(*tabular.cellInset(cur.idx()));
5601                 ParagraphList::const_iterator pit = itc.paragraphs().begin();
5602                 ParagraphList::const_iterator pend = itc.paragraphs().end();
5603                 for (; pit != pend; ++pit) {
5604                         InsetList::const_iterator it  = pit->insetList().begin();
5605                         InsetList::const_iterator end = pit->insetList().end();
5606                         for (; it != end; ++it) {
5607                                 if (it->inset->lyxCode() == CAPTION_CODE) {
5608                                         have_caption = true;
5609                                         break;
5610                                 }
5611                         }
5612                 }
5613                 status.setEnabled(!have_caption);
5614                 return true;
5615         }
5616
5617         // These are only enabled inside tabular
5618         case LFUN_CELL_BACKWARD:
5619         case LFUN_CELL_FORWARD:
5620                 status.setEnabled(true);
5621                 return true;
5622
5623         // disable these with multiple cells selected
5624         case LFUN_INSET_INSERT:
5625         case LFUN_TABULAR_INSERT:
5626         case LFUN_TABULAR_STYLE_INSERT:
5627         case LFUN_FLEX_INSERT:
5628         case LFUN_FLOAT_INSERT:
5629         case LFUN_FLOAT_WIDE_INSERT:
5630         case LFUN_FOOTNOTE_INSERT:
5631         case LFUN_MARGINALNOTE_INSERT:
5632         case LFUN_MATH_INSERT:
5633         case LFUN_MATH_MODE:
5634         case LFUN_MATH_MUTATE:
5635         case LFUN_MATH_DISPLAY:
5636         case LFUN_NOTE_INSERT:
5637         case LFUN_ARGUMENT_INSERT:
5638         case LFUN_BOX_INSERT:
5639         case LFUN_BRANCH_INSERT:
5640         case LFUN_PHANTOM_INSERT:
5641         case LFUN_WRAP_INSERT:
5642         case LFUN_PREVIEW_INSERT:
5643         case LFUN_ERT_INSERT: {
5644                 if (cur.selIsMultiCell()) {
5645                         status.setEnabled(false);
5646                         return true;
5647                 } else
5648                         return cell(cur.idx())->getStatus(cur, cmd, status);
5649         }
5650
5651         // disable in non-fixed-width cells
5652         case LFUN_PARAGRAPH_BREAK:
5653                 // multirow does not allow paragraph breaks
5654                 if (tabular.isMultiRow(cur.idx())) {
5655                         status.setEnabled(false);
5656                         return true;
5657                 }
5658                 // fall through
5659         case LFUN_NEWLINE_INSERT:
5660                 if ((tabular.isMultiColumn(cur.idx()) || tabular.isMultiRow(cur.idx()))
5661                     && tabular.getPWidth(cur.idx()).zero()) {
5662                         status.setEnabled(false);
5663                         return true;
5664                 }
5665                 return cell(cur.idx())->getStatus(cur, cmd, status);
5666
5667         case LFUN_NEWPAGE_INSERT:
5668                 status.setEnabled(false);
5669                 return true;
5670
5671         case LFUN_PASTE:
5672                 if (tabularStackDirty() && theClipboard().isInternal()) {
5673                         if (cur.selIsMultiCell()) {
5674                                 row_type rs, re;
5675                                 col_type cs, ce;
5676                                 getSelection(cur, rs, re, cs, ce);
5677                                 if (paste_tabular && paste_tabular->ncols() == ce - cs + 1
5678                                           && paste_tabular->nrows() == re - rs + 1)
5679                                         status.setEnabled(true);
5680                                 else {
5681                                         status.setEnabled(false);
5682                                         status.message(_("Selection size should match clipboard content."));
5683                                 }
5684                         } else
5685                                 status.setEnabled(true);
5686                         return true;
5687                 }
5688                 return cell(cur.idx())->getStatus(cur, cmd, status);
5689
5690         case LFUN_INSET_SETTINGS:
5691                 // relay this lfun to Inset, not to the cell.
5692                 return Inset::getStatus(cur, cmd, status);
5693
5694         default:
5695                 // we try to handle this event in the insets dispatch function.
5696                 return cell(cur.idx())->getStatus(cur, cmd, status);
5697         }
5698         return false;
5699 }
5700
5701
5702 Inset::DisplayType InsetTabular::display() const
5703 {
5704                 if (tabular.is_long_tabular) {
5705                         switch (tabular.longtabular_alignment) {
5706                         case Tabular::LYX_LONGTABULAR_ALIGN_LEFT:
5707                                 return AlignLeft;
5708                         case Tabular::LYX_LONGTABULAR_ALIGN_CENTER:
5709                                 return AlignCenter;
5710                         case Tabular::LYX_LONGTABULAR_ALIGN_RIGHT:
5711                                 return AlignRight;
5712                         default:
5713                                 return AlignCenter;
5714                         }
5715                 } else
5716                         return Inline;
5717 }
5718
5719
5720 void InsetTabular::latex(otexstream & os, OutputParams const & runparams) const
5721 {
5722         tabular.latex(os, runparams);
5723 }
5724
5725
5726 int InsetTabular::plaintext(odocstringstream & os,
5727         OutputParams const & runparams, size_t max_length) const
5728 {
5729         os << '\n'; // output table on a new line
5730         int const dp = runparams.linelen > 0 ? runparams.depth : 0;
5731         tabular.plaintext(os, runparams, dp, false, 0, max_length);
5732         return PLAINTEXT_NEWLINE;
5733 }
5734
5735
5736 int InsetTabular::docbook(odocstream & os, OutputParams const & runparams) const
5737 {
5738         int ret = 0;
5739         Inset * master = 0;
5740
5741         // FIXME: Why not pass a proper DocIterator here?
5742 #if 0
5743         // if the table is inside a float it doesn't need the informaltable
5744         // wrapper. Search for it.
5745         for (master = owner(); master; master = master->owner())
5746                 if (master->lyxCode() == FLOAT_CODE)
5747                         break;
5748 #endif
5749
5750         if (!master) {
5751                 os << "<informaltable>";
5752                 ++ret;
5753         }
5754         ret += tabular.docbook(os, runparams);
5755         if (!master) {
5756                 os << "</informaltable>";
5757                 ++ret;
5758         }
5759         return ret;
5760 }
5761
5762
5763 docstring InsetTabular::xhtml(XHTMLStream & xs, OutputParams const & rp) const
5764 {
5765         return tabular.xhtml(xs, rp);
5766 }
5767
5768
5769 void InsetTabular::validate(LaTeXFeatures & features) const
5770 {
5771         tabular.validate(features);
5772         features.useInsetLayout(getLayout());
5773 }
5774
5775
5776 shared_ptr<InsetTableCell const> InsetTabular::cell(idx_type idx) const
5777 {
5778         return tabular.cellInset(idx);
5779 }
5780
5781
5782 shared_ptr<InsetTableCell> InsetTabular::cell(idx_type idx)
5783 {
5784         return tabular.cellInset(idx);
5785 }
5786
5787
5788 void InsetTabular::cursorPos(BufferView const & bv,
5789                 CursorSlice const & sl, bool boundary, int & x, int & y) const
5790 {
5791         cell(sl.idx())->cursorPos(bv, sl, boundary, x, y);
5792
5793         // y offset     correction
5794         y += cellYPos(sl.idx());
5795         y += tabular.textVOffset(sl.idx());
5796         y += tabular.offsetVAlignment();
5797
5798         // x offset correction
5799         x += cellXPos(sl.idx());
5800         x += tabular.textHOffset(sl.idx());
5801         x += ADD_TO_TABULAR_WIDTH;
5802 }
5803
5804
5805 int InsetTabular::dist(BufferView & bv, idx_type const cell, int x, int y) const
5806 {
5807         int xx = 0;
5808         int yy = 0;
5809         Inset const & inset = *tabular.cellInset(cell);
5810         Point o = bv.coordCache().getInsets().xy(&inset);
5811         int const xbeg = o.x_ - tabular.textHOffset(cell);
5812         int const xend = xbeg + tabular.cellWidth(cell);
5813         row_type const row = tabular.cellRow(cell);
5814         int const ybeg = o.y_ - tabular.rowAscent(row)
5815                 - tabular.interRowSpace(row) - tabular.textVOffset(cell);
5816         int const yend = ybeg + tabular.cellHeight(cell);
5817
5818         if (x < xbeg)
5819                 xx = xbeg - x;
5820         else if (x > xend)
5821                 xx = x - xend;
5822
5823         if (y < ybeg)
5824                 yy = ybeg - y;
5825         else if (y > yend)
5826                 yy = y - yend;
5827
5828         //lyxerr << " xbeg=" << xbeg << "  xend=" << xend
5829         //       << " ybeg=" << ybeg << " yend=" << yend
5830         //       << " xx=" << xx << " yy=" << yy
5831         //       << " dist=" << xx + yy << endl;
5832         return xx + yy;
5833 }
5834
5835
5836 Inset * InsetTabular::editXY(Cursor & cur, int x, int y)
5837 {
5838         //lyxerr << "InsetTabular::editXY: " << this << endl;
5839         cur.push(*this);
5840         cur.idx() = getNearestCell(cur.bv(), x, y);
5841         return cur.bv().textMetrics(&cell(cur.idx())->text()).editXY(cur, x, y);
5842 }
5843
5844
5845 void InsetTabular::setCursorFromCoordinates(Cursor & cur, int x, int y) const
5846 {
5847         cur.idx() = getNearestCell(cur.bv(), x, y);
5848         cur.bv().textMetrics(&cell(cur.idx())->text()).setCursorFromCoordinates(cur, x, y);
5849 }
5850
5851
5852 InsetTabular::idx_type InsetTabular::getNearestCell(BufferView & bv, int x, int y) const
5853 {
5854         idx_type idx_min = 0;
5855         int dist_min = numeric_limits<int>::max();
5856         for (idx_type i = 0, n = nargs(); i != n; ++i) {
5857                 if (bv.coordCache().getInsets().has(tabular.cellInset(i).get())) {
5858                         int const d = dist(bv, i, x, y);
5859                         if (d < dist_min) {
5860                                 dist_min = d;
5861                                 idx_min = i;
5862                         }
5863                 }
5864         }
5865         return idx_min;
5866 }
5867
5868
5869 int InsetTabular::cellYPos(idx_type const cell) const
5870 {
5871         row_type row = tabular.cellRow(cell);
5872         int ly = 0;
5873         for (row_type r = 0; r < row; ++r)
5874                 ly += tabular.rowDescent(r) + tabular.rowAscent(r + 1)
5875                         + tabular.interRowSpace(r + 1);
5876         return ly;
5877 }
5878
5879
5880 int InsetTabular::cellXPos(idx_type const cell) const
5881 {
5882         col_type col = tabular.cellColumn(cell);
5883         int lx = 0;
5884         for (col_type c = 0; c < col; ++c)
5885                 lx += tabular.column_info[c].width;
5886         return lx;
5887 }
5888
5889
5890 void InsetTabular::moveNextCell(Cursor & cur, EntryDirection entry_from)
5891 {
5892         row_type const row = tabular.cellRow(cur.idx());
5893         col_type const col = tabular.cellColumn(cur.idx());
5894
5895         if (isRightToLeft(cur)) {
5896                 if (tabular.cellColumn(cur.idx()) == 0) {
5897                         if (row == tabular.nrows() - 1)
5898                                 return;
5899                         cur.idx() = tabular.cellBelow(tabular.getLastCellInRow(row));
5900                 } else {
5901                         if (cur.idx() == 0)
5902                                 return;
5903                         if (col == 0)
5904                                 cur.idx() = tabular.getLastCellInRow(row - 1);
5905                         else
5906                                 cur.idx() = tabular.cellIndex(row, col - 1);
5907                 }
5908         } else {
5909                 if (tabular.isLastCell(cur.idx()))
5910                         return;
5911                 if (cur.idx() == tabular.getLastCellInRow(row))
5912                         cur.idx() = tabular.cellIndex(row + 1, 0);
5913                 else {
5914                         col_type const colnextcell = col + tabular.columnSpan(cur.idx());
5915                         cur.idx() = tabular.cellIndex(row, colnextcell);
5916                 }
5917         }
5918
5919         cur.boundary(false);
5920
5921         if (cur.selIsMultiCell()) {
5922                 cur.pit() = cur.lastpit();
5923                 cur.pos() = cur.lastpos();
5924                 return;
5925         }
5926
5927         cur.pit() = 0;
5928         cur.pos() = 0;
5929
5930         // in visual mode, place cursor at extreme left or right
5931
5932         switch(entry_from) {
5933
5934         case ENTRY_DIRECTION_RIGHT:
5935                 cur.posVisToRowExtremity(false /* !left */);
5936                 break;
5937         case ENTRY_DIRECTION_LEFT:
5938                 cur.posVisToRowExtremity(true /* left */);
5939                 break;
5940         case ENTRY_DIRECTION_IGNORE:
5941                 // nothing to do in this case
5942                 break;
5943
5944         }
5945         cur.setCurrentFont();
5946 }
5947
5948
5949 void InsetTabular::movePrevCell(Cursor & cur, EntryDirection entry_from)
5950 {
5951         row_type const row = tabular.cellRow(cur.idx());
5952         col_type const col = tabular.cellColumn(cur.idx());
5953
5954         if (isRightToLeft(cur)) {
5955                 if (cur.idx() == tabular.getLastCellInRow(row)) {
5956                         if (row == 0)
5957                                 return;
5958                         cur.idx() = tabular.getFirstCellInRow(row);
5959                         cur.idx() = tabular.cellAbove(cur.idx());
5960                 } else {
5961                         if (tabular.isLastCell(cur.idx()))
5962                                 return;
5963                         if (cur.idx() == tabular.getLastCellInRow(row))
5964                                 cur.idx() = tabular.cellIndex(row + 1, 0);
5965                         else
5966                                 cur.idx() = tabular.cellIndex(row, col + 1);
5967                 }
5968         } else {
5969                 if (cur.idx() == 0) // first cell
5970                         return;
5971                 if (col == 0)
5972                         cur.idx() = tabular.getLastCellInRow(row - 1);
5973                 else
5974                         cur.idx() = tabular.cellIndex(row, col - 1);
5975         }
5976
5977         if (cur.selIsMultiCell()) {
5978                 cur.pit() = cur.lastpit();
5979                 cur.pos() = cur.lastpos();
5980                 return;
5981         }
5982
5983         cur.pit() = cur.lastpit();
5984         cur.pos() = cur.lastpos();
5985
5986         // in visual mode, place cursor at extreme left or right
5987
5988         switch(entry_from) {
5989
5990         case ENTRY_DIRECTION_RIGHT:
5991                 cur.posVisToRowExtremity(false /* !left */);
5992                 break;
5993         case ENTRY_DIRECTION_LEFT:
5994                 cur.posVisToRowExtremity(true /* left */);
5995                 break;
5996         case ENTRY_DIRECTION_IGNORE:
5997                 // nothing to do in this case
5998                 break;
5999
6000         }
6001         cur.setCurrentFont();
6002 }
6003
6004
6005 void InsetTabular::tabularFeatures(Cursor & cur, string const & argument)
6006 {
6007         cur.recordUndoInset(this);
6008
6009         istringstream is(argument);
6010         // limit the size of strings we read to avoid memory problems
6011         is >> setw(65636);
6012         string s;
6013         // Safe guard.
6014         size_t safe_guard = 0;
6015         for (;;) {
6016                 if (is.eof())
6017                         return;
6018                 safe_guard++;
6019                 if (safe_guard > 1000) {
6020                         LYXERR0("parameter max count reached!");
6021                         return;
6022                 }
6023                 is >> s;
6024                 Tabular::Feature action = Tabular::LAST_ACTION;
6025
6026                 size_t i = 0;
6027                 for (; tabularFeature[i].action != Tabular::LAST_ACTION; ++i) {
6028                         if (s != tabularFeature[i].feature)
6029                                 continue;
6030                         action = tabularFeature[i].action;
6031                         break;
6032                 }
6033                 if (action == Tabular::LAST_ACTION) {
6034                         LYXERR0("Feature not found " << s);
6035                         continue;
6036                 }
6037                 string val;
6038                 if (tabularFeature[i].need_value)
6039                         is >> val;
6040                 LYXERR(Debug::DEBUG, "Feature: " << s << "\t\tvalue: " << val);
6041                 tabularFeatures(cur, action, val);
6042         }
6043 }
6044
6045
6046 static void checkLongtableSpecial(Tabular::ltType & ltt,
6047                           string const & special, bool & flag)
6048 {
6049         if (special == "dl_above") {
6050                 ltt.topDL = flag;
6051                 ltt.set = false;
6052         } else if (special == "dl_below") {
6053                 ltt.bottomDL = flag;
6054                 ltt.set = false;
6055         } else if (special == "empty") {
6056                 ltt.empty = flag;
6057                 ltt.set = false;
6058         } else if (flag) {
6059                 ltt.empty = false;
6060                 ltt.set = true;
6061         }
6062 }
6063
6064
6065 bool InsetTabular::oneCellHasRotationState(bool rotated,
6066                 row_type row_start, row_type row_end,
6067                 col_type col_start, col_type col_end) const
6068 {
6069         for (row_type r = row_start; r <= row_end; ++r)
6070                 for (col_type c = col_start; c <= col_end; ++c)
6071                         if (rotated) {
6072                                 if (tabular.getRotateCell(tabular.cellIndex(r, c)) != 0)
6073                                         return true;
6074                         } else {
6075                                 if (tabular.getRotateCell(tabular.cellIndex(r, c)) == 0)
6076                                         return true;
6077                         }
6078         return false;
6079 }
6080
6081
6082 void InsetTabular::tabularFeatures(Cursor & cur,
6083         Tabular::Feature feature, string const & value)
6084 {
6085         col_type sel_col_start;
6086         col_type sel_col_end;
6087         row_type sel_row_start;
6088         row_type sel_row_end;
6089         bool setLines = false;
6090         bool setLinesInnerOnly = false;
6091         LyXAlignment setAlign = LYX_ALIGN_LEFT;
6092         Tabular::VAlignment setVAlign = Tabular::LYX_VALIGN_TOP;
6093
6094         switch (feature) {
6095
6096         case Tabular::M_ALIGN_LEFT:
6097         case Tabular::ALIGN_LEFT:
6098                 setAlign = LYX_ALIGN_LEFT;
6099                 break;
6100
6101         case Tabular::M_ALIGN_RIGHT:
6102         case Tabular::ALIGN_RIGHT:
6103                 setAlign = LYX_ALIGN_RIGHT;
6104                 break;
6105
6106         case Tabular::M_ALIGN_CENTER:
6107         case Tabular::ALIGN_CENTER:
6108                 setAlign = LYX_ALIGN_CENTER;
6109                 break;
6110
6111         case Tabular::ALIGN_BLOCK:
6112                 setAlign = LYX_ALIGN_BLOCK;
6113                 break;
6114
6115         case Tabular::ALIGN_DECIMAL:
6116                 setAlign = LYX_ALIGN_DECIMAL;
6117                 break;
6118
6119         case Tabular::M_VALIGN_TOP:
6120         case Tabular::VALIGN_TOP:
6121                 setVAlign = Tabular::LYX_VALIGN_TOP;
6122                 break;
6123
6124         case Tabular::M_VALIGN_BOTTOM:
6125         case Tabular::VALIGN_BOTTOM:
6126                 setVAlign = Tabular::LYX_VALIGN_BOTTOM;
6127                 break;
6128
6129         case Tabular::M_VALIGN_MIDDLE:
6130         case Tabular::VALIGN_MIDDLE:
6131                 setVAlign = Tabular::LYX_VALIGN_MIDDLE;
6132                 break;
6133
6134         default:
6135                 break;
6136         }
6137
6138         getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
6139         row_type const row = tabular.cellRow(cur.idx());
6140         col_type const column = tabular.cellColumn(cur.idx());
6141         bool flag = true;
6142         Tabular::ltType ltt;
6143
6144         switch (feature) {
6145
6146         case Tabular::SET_TABULAR_WIDTH:
6147                 tabular.setTabularWidth(Length(value));
6148                 break;
6149
6150         case Tabular::SET_PWIDTH: {
6151                 Length const len(value);
6152                 for (col_type c = sel_col_start; c <= sel_col_end; ++c) {
6153                         tabular.setColumnPWidth(cur, tabular.cellIndex(row, c), len);
6154                         if (len.zero()
6155                             && tabular.getAlignment(tabular.cellIndex(row, c), true) == LYX_ALIGN_BLOCK)
6156                                 tabularFeatures(cur, Tabular::ALIGN_CENTER, string());
6157                 }
6158                 break;
6159         }
6160
6161         case Tabular::SET_MPWIDTH:
6162                 tabular.setMColumnPWidth(cur, cur.idx(), Length(value));
6163                 break;
6164
6165         case Tabular::TOGGLE_VARWIDTH_COLUMN: {
6166                 bool const varwidth = value == "on";
6167                 for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6168                         tabular.toggleVarwidth(tabular.cellIndex(row, c), varwidth);
6169                 break;
6170         }
6171
6172         case Tabular::SET_MROFFSET:
6173                 tabular.setMROffset(cur, cur.idx(), Length(value));
6174                 break;
6175
6176         case Tabular::SET_SPECIAL_COLUMN:
6177         case Tabular::SET_SPECIAL_MULTICOLUMN:
6178                 if (value == "none")
6179                         tabular.setAlignSpecial(cur.idx(), docstring(), feature);
6180                 else
6181                         tabular.setAlignSpecial(cur.idx(), from_utf8(value), feature);
6182                 break;
6183
6184         case Tabular::APPEND_ROW:
6185                 // append the row into the tabular
6186                 tabular.appendRow(row);
6187                 break;
6188
6189         case Tabular::APPEND_COLUMN:
6190                 // append the column into the tabular
6191                 tabular.appendColumn(column);
6192                 cur.idx() = tabular.cellIndex(row, column);
6193                 break;
6194
6195         case Tabular::DELETE_ROW:
6196                 if (sel_row_end == tabular.nrows() - 1 && sel_row_start != 0) {
6197                         for (col_type c = 0; c < tabular.ncols(); c++) {
6198                                 tabular.setBottomLine(tabular.cellIndex(sel_row_start - 1, c),
6199                                         tabular.bottomLine(tabular.cellIndex(sel_row_end, c)));
6200                                 tabular.setBottomLineTrim(tabular.cellIndex(sel_row_start - 1, c),
6201                                         tabular.bottomLineTrim(tabular.cellIndex(sel_row_end, c)));
6202                         }
6203                 }
6204
6205                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6206                         tabular.deleteRow(sel_row_start);
6207                 if (sel_row_start >= tabular.nrows())
6208                         --sel_row_start;
6209                 cur.idx() = tabular.cellIndex(sel_row_start, column);
6210                 cur.pit() = 0;
6211                 cur.pos() = 0;
6212                 cur.selection(false);
6213                 break;
6214
6215         case Tabular::DELETE_COLUMN:
6216                 if (sel_col_end == tabular.ncols() - 1 && sel_col_start != 0) {
6217                         for (row_type r = 0; r < tabular.nrows(); r++)
6218                                 tabular.setRightLine(tabular.cellIndex(r, sel_col_start - 1),
6219                                         tabular.rightLine(tabular.cellIndex(r, sel_col_end)));
6220                 }
6221
6222                 if (sel_col_start == 0 && sel_col_end != tabular.ncols() - 1) {
6223                         for (row_type r = 0; r < tabular.nrows(); r++)
6224                                 tabular.setLeftLine(tabular.cellIndex(r, sel_col_end + 1),
6225                                         tabular.leftLine(tabular.cellIndex(r, 0)));
6226                 }
6227
6228                 for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6229                         tabular.deleteColumn(sel_col_start);
6230                 if (sel_col_start >= tabular.ncols())
6231                         --sel_col_start;
6232                 cur.idx() = tabular.cellIndex(row, sel_col_start);
6233                 cur.pit() = 0;
6234                 cur.pos() = 0;
6235                 cur.selection(false);
6236                 break;
6237
6238         case Tabular::COPY_ROW:
6239                 tabular.copyRow(row);
6240                 break;
6241
6242         case Tabular::COPY_COLUMN:
6243                 tabular.copyColumn(column);
6244                 cur.idx() = tabular.cellIndex(row, column);
6245                 break;
6246
6247         case Tabular::MOVE_COLUMN_RIGHT:
6248                 tabular.moveColumn(column, Tabular::RIGHT);
6249                 cur.idx() = tabular.cellIndex(row, column + 1);
6250                 break;
6251
6252         case Tabular::MOVE_COLUMN_LEFT:
6253                 tabular.moveColumn(column, Tabular::LEFT);
6254                 cur.idx() = tabular.cellIndex(row, column - 1);
6255                 break;
6256
6257         case Tabular::MOVE_ROW_DOWN:
6258                 tabular.moveRow(row, Tabular::DOWN);
6259                 cur.idx() = tabular.cellIndex(row + 1, column);
6260                 break;
6261
6262         case Tabular::MOVE_ROW_UP:
6263                 tabular.moveRow(row, Tabular::UP);
6264                 cur.idx() = tabular.cellIndex(row - 1, column);
6265                 break;
6266
6267         case Tabular::SET_LINE_TOP:
6268         case Tabular::TOGGLE_LINE_TOP: {
6269                 bool lineSet = (feature == Tabular::SET_LINE_TOP)
6270                                ? (value == "true") : !tabular.topLine(cur.idx());
6271                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6272                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6273                                 tabular.setTopLine(tabular.cellIndex(r, c), lineSet);
6274                 break;
6275         }
6276
6277         case Tabular::SET_LINE_BOTTOM:
6278         case Tabular::TOGGLE_LINE_BOTTOM: {
6279                 bool lineSet = (feature == Tabular::SET_LINE_BOTTOM)
6280                                ? (value == "true") : !tabular.bottomLine(cur.idx());
6281                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6282                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6283                                 tabular.setBottomLine(tabular.cellIndex(r, c), lineSet);
6284                 break;
6285         }
6286
6287         case Tabular::SET_LTRIM_TOP:
6288         case Tabular::TOGGLE_LTRIM_TOP: {
6289                 bool l = (feature == Tabular::SET_LTRIM_TOP)
6290                                ? (value == "true") : !tabular.topLineTrim(cur.idx()).first;
6291                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6292                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6293                                 tabular.setTopLineLTrim(tabular.cellIndex(r, c), l);
6294                 break;
6295         }
6296
6297         case Tabular::SET_RTRIM_TOP:
6298         case Tabular::TOGGLE_RTRIM_TOP: {
6299                 bool l = (feature == Tabular::SET_RTRIM_TOP)
6300                                ? (value == "true") : !tabular.topLineTrim(cur.idx()).second;
6301                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6302                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6303                                 tabular.setTopLineRTrim(tabular.cellIndex(r, c), l);
6304                 break;
6305         }
6306
6307         case Tabular::SET_LTRIM_BOTTOM:
6308         case Tabular::TOGGLE_LTRIM_BOTTOM: {
6309                 bool l = (feature == Tabular::SET_LTRIM_BOTTOM)
6310                                ? (value == "true") : !tabular.bottomLineTrim(cur.idx()).first;
6311                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6312                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6313                                 tabular.setBottomLineLTrim(tabular.cellIndex(r, c), l);
6314                 break;
6315         }
6316
6317         case Tabular::SET_RTRIM_BOTTOM:
6318         case Tabular::TOGGLE_RTRIM_BOTTOM: {
6319                 bool l = (feature == Tabular::SET_RTRIM_BOTTOM)
6320                                ? (value == "true") : !tabular.bottomLineTrim(cur.idx()).second;
6321                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6322                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6323                                 tabular.setBottomLineRTrim(tabular.cellIndex(r, c), l);
6324                 break;
6325         }
6326
6327         case Tabular::SET_LINE_LEFT:
6328         case Tabular::TOGGLE_LINE_LEFT: {
6329                 bool lineSet = (feature == Tabular::SET_LINE_LEFT)
6330                                ? (value == "true") : !tabular.leftLine(cur.idx());
6331                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6332                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6333                                 tabular.setLeftLine(tabular.cellIndex(r, c), lineSet);
6334                 break;
6335         }
6336
6337         case Tabular::SET_LINE_RIGHT:
6338         case Tabular::TOGGLE_LINE_RIGHT: {
6339                 bool lineSet = (feature == Tabular::SET_LINE_RIGHT)
6340                                ? (value == "true") : !tabular.rightLine(cur.idx());
6341                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6342                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6343                                 tabular.setRightLine(tabular.cellIndex(r, c), lineSet);
6344                 break;
6345         }
6346
6347         case Tabular::M_ALIGN_LEFT:
6348         case Tabular::M_ALIGN_RIGHT:
6349         case Tabular::M_ALIGN_CENTER:
6350         case Tabular::ALIGN_LEFT:
6351         case Tabular::ALIGN_RIGHT:
6352         case Tabular::ALIGN_CENTER:
6353         case Tabular::ALIGN_BLOCK:
6354         case Tabular::ALIGN_DECIMAL:
6355                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6356                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6357                                 tabular.setAlignment(tabular.cellIndex(r, c), setAlign,
6358                                 !tabular.getPWidth(c).zero());
6359                 break;
6360
6361         case Tabular::M_VALIGN_TOP:
6362         case Tabular::M_VALIGN_BOTTOM:
6363         case Tabular::M_VALIGN_MIDDLE:
6364                 flag = false;
6365                 // fall through
6366         case Tabular::VALIGN_TOP:
6367         case Tabular::VALIGN_BOTTOM:
6368         case Tabular::VALIGN_MIDDLE:
6369                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6370                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6371                                 tabular.setVAlignment(tabular.cellIndex(r, c), setVAlign, flag);
6372                 break;
6373
6374         case Tabular::SET_MULTICOLUMN: {
6375                 if (!cur.selection()) {
6376                         // just multicol for one single cell
6377                         // check whether we are completely in a multicol
6378                         if (!tabular.isMultiColumn(cur.idx()))
6379                                 tabular.setMultiColumn(cur, cur.idx(), 1,
6380                                         tabular.rightLine(cur.idx()));
6381                         break;
6382                 }
6383                 // we have a selection so this means we just add all these
6384                 // cells to form a multicolumn cell
6385                 idx_type const s_start = cur.selBegin().idx();
6386                 row_type const col_start = tabular.cellColumn(s_start);
6387                 row_type const col_end = tabular.cellColumn(cur.selEnd().idx());
6388                 cur.idx() = tabular.setMultiColumn(cur, s_start, col_end - col_start + 1,
6389                                                    tabular.rightLine(cur.selEnd().idx()));
6390                 cur.pit() = 0;
6391                 cur.pos() = 0;
6392                 cur.selection(false);
6393                 break;
6394         }
6395
6396         case Tabular::UNSET_MULTICOLUMN: {
6397                 if (!cur.selection()) {
6398                         if (tabular.isMultiColumn(cur.idx()))
6399                                 tabular.unsetMultiColumn(cur.idx());
6400                 }
6401                 break;
6402         }
6403
6404         case Tabular::MULTICOLUMN: {
6405                 if (!cur.selection()) {
6406                         if (tabular.isMultiColumn(cur.idx()))
6407                                 tabularFeatures(cur, Tabular::UNSET_MULTICOLUMN);
6408                         else
6409                                 tabularFeatures(cur, Tabular::SET_MULTICOLUMN);
6410                         break;
6411                 }
6412                 bool merge = false;
6413                 for (col_type c = sel_col_start; c <= sel_col_end; ++c) {
6414                         row_type const r = sel_row_start;
6415                         if (!tabular.isMultiColumn(tabular.cellIndex(r, c))
6416                             || (r > sel_row_start && !tabular.isPartOfMultiColumn(r, c)))
6417                                 merge = true;
6418                 }
6419                 // If the selection contains at least one singlecol cell
6420                 // or multiple multicol cells,
6421                 // we assume the user will merge is to a single multicol
6422                 if (merge)
6423                         tabularFeatures(cur, Tabular::SET_MULTICOLUMN);
6424                 else
6425                         tabularFeatures(cur, Tabular::UNSET_MULTICOLUMN);
6426                 break;
6427         }
6428
6429         case Tabular::SET_MULTIROW: {
6430                 if (!cur.selection()) {
6431                         // just multirow for one single cell
6432                         // check whether we are completely in a multirow
6433                         if (!tabular.isMultiRow(cur.idx()))
6434                                 tabular.setMultiRow(cur, cur.idx(), 1,
6435                                                     tabular.bottomLine(cur.idx()),
6436                                                     tabular.getAlignment(cur.idx()));
6437                         break;
6438                 }
6439                 // we have a selection so this means we just add all this
6440                 // cells to form a multirow cell
6441                 idx_type const s_start = cur.selBegin().idx();
6442                 row_type const row_start = tabular.cellRow(s_start);
6443                 row_type const row_end = tabular.cellRow(cur.selEnd().idx());
6444                 cur.idx() = tabular.setMultiRow(cur, s_start, row_end - row_start + 1,
6445                                                 tabular.bottomLine(cur.selEnd().idx()),
6446                                                 tabular.getAlignment(cur.selEnd().idx()));
6447                 cur.pit() = 0;
6448                 cur.pos() = 0;
6449                 cur.selection(false);
6450                 break;
6451         }
6452
6453         case Tabular::UNSET_MULTIROW: {
6454                 if (!cur.selection()) {
6455                         if (tabular.isMultiRow(cur.idx()))
6456                                 tabular.unsetMultiRow(cur.idx());
6457                 }
6458                 break;
6459         }
6460
6461         case Tabular::MULTIROW: {
6462                 if (!cur.selection()) {
6463                         if (tabular.isMultiRow(cur.idx()))
6464                                 tabularFeatures(cur, Tabular::UNSET_MULTIROW);
6465                         else
6466                                 tabularFeatures(cur, Tabular::SET_MULTIROW);
6467                         break;
6468                 }
6469                 bool merge = false;
6470                 for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
6471                         col_type const c = sel_col_start;
6472                         if (!tabular.isMultiRow(tabular.cellIndex(r, c))
6473                             || (r > sel_row_start && !tabular.isPartOfMultiRow(r, c)))
6474                                 merge = true;
6475                 }
6476                 // If the selection contains at least one singlerow cell
6477                 // or multiple multirow cells,
6478                 // we assume the user will merge is to a single multirow
6479                 if (merge)
6480                         tabularFeatures(cur, Tabular::SET_MULTIROW);
6481                 else
6482                         tabularFeatures(cur, Tabular::UNSET_MULTIROW);
6483                 break;
6484         }
6485
6486         case Tabular::SET_INNER_LINES:
6487                 setLinesInnerOnly = true;
6488                 // fall through
6489         case Tabular::SET_ALL_LINES:
6490                 setLines = true;
6491                 // fall through
6492         case Tabular::UNSET_ALL_LINES:
6493                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6494                         for (col_type c = sel_col_start; c <= sel_col_end; ++c) {
6495                                 idx_type const cell = tabular.cellIndex(r, c);
6496                                 if (!setLinesInnerOnly || r != sel_row_start)
6497                                         tabular.setTopLine(cell, setLines);
6498                                 if ((!setLinesInnerOnly || r != sel_row_end)
6499                                     && (!setLines || r == sel_row_end))
6500                                         tabular.setBottomLine(cell, setLines);
6501                                 if ((!setLinesInnerOnly || c != sel_col_end)
6502                                     && (!setLines || c == sel_col_end))
6503                                         tabular.setRightLine(cell, setLines);
6504                                 if ((!setLinesInnerOnly || c != sel_col_start))
6505                                         tabular.setLeftLine(cell, setLines);
6506                         }
6507                 break;
6508
6509         case Tabular::RESET_FORMAL_DEFAULT:
6510                 for (row_type r = 0; r < tabular.nrows(); ++r) {
6511                         bool const head_or_foot = r == 0 || r == tabular.nrows() - 1;
6512                         for (col_type c = 0; c < tabular.ncols(); ++c) {
6513                                 idx_type const cell = tabular.cellIndex(r, c);
6514                                 tabular.setTopLine(cell, head_or_foot);
6515                                 tabular.setBottomLine(cell, head_or_foot);
6516                         }
6517                 }
6518                 break;
6519
6520         case Tabular::SET_BORDER_LINES:
6521                 for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
6522                         tabular.setLeftLine(tabular.cellIndex(r, sel_col_start), true);
6523                         tabular.setRightLine(tabular.cellIndex(r, sel_col_end), true);
6524                 }
6525                 for (col_type c = sel_col_start; c <= sel_col_end; ++c) {
6526                         tabular.setTopLine(tabular.cellIndex(sel_row_start, c), true);
6527                         tabular.setBottomLine(tabular.cellIndex(sel_row_end, c), true);
6528                 }
6529                 break;
6530
6531         case Tabular::TOGGLE_LONGTABULAR:
6532                 if (tabular.is_long_tabular)
6533                         tabularFeatures(cur, Tabular::UNSET_LONGTABULAR);
6534                 else
6535                         tabular.is_long_tabular = true;
6536                 break;
6537
6538         case Tabular::SET_LONGTABULAR:
6539                 tabular.is_long_tabular = true;
6540                 break;
6541
6542         case Tabular::UNSET_LONGTABULAR:
6543                 for (row_type r = 0; r < tabular.nrows(); ++r) {
6544                         if (tabular.ltCaption(r)) {
6545                                 cur.idx() = tabular.cellIndex(r, 0);
6546                                 cur.pit() = 0;
6547                                 cur.pos() = 0;
6548                                 tabularFeatures(cur, Tabular::TOGGLE_LTCAPTION);
6549                         }
6550                 }
6551                 tabular.is_long_tabular = false;
6552                 break;
6553
6554         case Tabular::SET_ROTATE_TABULAR:
6555                 tabular.rotate = convert<int>(value);
6556                 break;
6557
6558         case Tabular::UNSET_ROTATE_TABULAR:
6559                 tabular.rotate = 0;
6560                 break;
6561
6562         case Tabular::TOGGLE_ROTATE_TABULAR:
6563                 // when pressing the rotate button we default to 90° rotation
6564                 tabular.rotate != 0 ? tabular.rotate = 0 : tabular.rotate = 90;
6565                 break;
6566
6567         case Tabular::TABULAR_VALIGN_TOP:
6568                 tabular.tabular_valignment = Tabular::LYX_VALIGN_TOP;
6569                 break;
6570
6571         case Tabular::TABULAR_VALIGN_MIDDLE:
6572                 tabular.tabular_valignment = Tabular::LYX_VALIGN_MIDDLE;
6573                 break;
6574
6575         case Tabular::TABULAR_VALIGN_BOTTOM:
6576                 tabular.tabular_valignment = Tabular::LYX_VALIGN_BOTTOM;
6577                 break;
6578
6579         case Tabular::LONGTABULAR_ALIGN_LEFT:
6580                 tabular.longtabular_alignment = Tabular::LYX_LONGTABULAR_ALIGN_LEFT;
6581                 break;
6582
6583         case Tabular::LONGTABULAR_ALIGN_CENTER:
6584                 tabular.longtabular_alignment = Tabular::LYX_LONGTABULAR_ALIGN_CENTER;
6585                 break;
6586
6587         case Tabular::LONGTABULAR_ALIGN_RIGHT:
6588                 tabular.longtabular_alignment = Tabular::LYX_LONGTABULAR_ALIGN_RIGHT;
6589                 break;
6590
6591         case Tabular::SET_ROTATE_CELL:
6592                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6593                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6594                                 tabular.setRotateCell(tabular.cellIndex(r, c), convert<int>(value));
6595                 break;
6596
6597         case Tabular::UNSET_ROTATE_CELL:
6598                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6599                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6600                                 tabular.setRotateCell(tabular.cellIndex(r, c), 0);
6601                 break;
6602
6603         case Tabular::TOGGLE_ROTATE_CELL:
6604                 {
6605                 bool oneNotRotated = oneCellHasRotationState(false,
6606                         sel_row_start, sel_row_end, sel_col_start, sel_col_end);
6607
6608                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6609                         for (col_type c = sel_col_start; c <= sel_col_end; ++c) {
6610                                 // when pressing the rotate cell button we default to 90° rotation
6611                                 if (oneNotRotated)
6612                                         tabular.setRotateCell(tabular.cellIndex(r, c), 90);
6613                                 else
6614                                         tabular.setRotateCell(tabular.cellIndex(r, c), 0);
6615                         }
6616                 }
6617                 break;
6618
6619         case Tabular::SET_USEBOX: {
6620                 Tabular::BoxType val = Tabular::BoxType(convert<int>(value));
6621                 if (val == tabular.getUsebox(cur.idx()))
6622                         val = Tabular::BOX_NONE;
6623                 for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6624                         for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6625                                 tabular.setUsebox(tabular.cellIndex(r, c), val);
6626                 break;
6627         }
6628
6629         case Tabular::UNSET_LTFIRSTHEAD:
6630                 flag = false;
6631                 // fall through
6632         case Tabular::SET_LTFIRSTHEAD:
6633                 tabular.getRowOfLTFirstHead(row, ltt);
6634                 checkLongtableSpecial(ltt, value, flag);
6635                 tabular.setLTHead(row, flag, ltt, true);
6636                 break;
6637
6638         case Tabular::UNSET_LTHEAD:
6639                 flag = false;
6640                 // fall through
6641         case Tabular::SET_LTHEAD:
6642                 tabular.getRowOfLTHead(row, ltt);
6643                 checkLongtableSpecial(ltt, value, flag);
6644                 tabular.setLTHead(row, flag, ltt, false);
6645                 break;
6646
6647         case Tabular::UNSET_LTFOOT:
6648                 flag = false;
6649                 // fall through
6650         case Tabular::SET_LTFOOT:
6651                 tabular.getRowOfLTFoot(row, ltt);
6652                 checkLongtableSpecial(ltt, value, flag);
6653                 tabular.setLTFoot(row, flag, ltt, false);
6654                 break;
6655
6656         case Tabular::UNSET_LTLASTFOOT:
6657                 flag = false;
6658                 // fall through
6659         case Tabular::SET_LTLASTFOOT:
6660                 tabular.getRowOfLTLastFoot(row, ltt);
6661                 checkLongtableSpecial(ltt, value, flag);
6662                 tabular.setLTFoot(row, flag, ltt, true);
6663                 break;
6664
6665         case Tabular::UNSET_LTNEWPAGE:
6666                 flag = false;
6667                 // fall through
6668         case Tabular::SET_LTNEWPAGE:
6669                 tabular.setLTNewPage(row, flag);
6670                 break;
6671
6672         case Tabular::SET_LTCAPTION: {
6673                 if (tabular.ltCaption(row))
6674                         break;
6675                 cur.idx() = tabular.setLTCaption(cur, row, true);
6676                 cur.pit() = 0;
6677                 cur.pos() = 0;
6678                 cur.selection(false);
6679                 // If a row is set as caption, then also insert
6680                 // a caption. Otherwise the LaTeX output is broken.
6681                 // Select cell if it is non-empty
6682                 if (cur.lastpos() > 0 || cur.lastpit() > 0)
6683                         lyx::dispatch(FuncRequest(LFUN_INSET_SELECT_ALL));
6684                 lyx::dispatch(FuncRequest(LFUN_CAPTION_INSERT));
6685                 break;
6686         }
6687
6688         case Tabular::UNSET_LTCAPTION: {
6689                 if (!tabular.ltCaption(row))
6690                         break;
6691                 cur.idx() = tabular.setLTCaption(cur, row, false);
6692                 cur.pit() = 0;
6693                 cur.pos() = 0;
6694                 cur.selection(false);
6695                 FuncRequest fr(LFUN_INSET_DISSOLVE, "caption");
6696                 if (lyx::getStatus(fr).enabled())
6697                         lyx::dispatch(fr);
6698                 break;
6699         }
6700
6701         case Tabular::TOGGLE_LTCAPTION: {
6702                 if (tabular.ltCaption(row))
6703                         tabularFeatures(cur, Tabular::UNSET_LTCAPTION);
6704                 else
6705                         tabularFeatures(cur, Tabular::SET_LTCAPTION);
6706                 break;
6707         }
6708
6709         case Tabular::TOGGLE_BOOKTABS:
6710                 tabular.use_booktabs = !tabular.use_booktabs;
6711                 break;
6712
6713         case Tabular::SET_BOOKTABS:
6714                 tabular.use_booktabs = true;
6715                 break;
6716
6717         case Tabular::UNSET_BOOKTABS:
6718                 tabular.use_booktabs = false;
6719                 break;
6720
6721         case Tabular::SET_TOP_SPACE: {
6722                 Length len;
6723                 if (value == "default")
6724                         for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6725                                 tabular.row_info[r].top_space_default = true;
6726                 else if (value == "none")
6727                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
6728                                 tabular.row_info[r].top_space_default = false;
6729                                 tabular.row_info[r].top_space = len;
6730                         }
6731                 else if (isValidLength(value, &len))
6732                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
6733                                 tabular.row_info[r].top_space_default = false;
6734                                 tabular.row_info[r].top_space = len;
6735                         }
6736                 break;
6737         }
6738
6739         case Tabular::SET_BOTTOM_SPACE: {
6740                 Length len;
6741                 if (value == "default")
6742                         for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6743                                 tabular.row_info[r].bottom_space_default = true;
6744                 else if (value == "none")
6745                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
6746                                 tabular.row_info[r].bottom_space_default = false;
6747                                 tabular.row_info[r].bottom_space = len;
6748                         }
6749                 else if (isValidLength(value, &len))
6750                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
6751                                 tabular.row_info[r].bottom_space_default = false;
6752                                 tabular.row_info[r].bottom_space = len;
6753                         }
6754                 break;
6755         }
6756
6757         case Tabular::SET_INTERLINE_SPACE: {
6758                 Length len;
6759                 if (value == "default")
6760                         for (row_type r = sel_row_start; r <= sel_row_end; ++r)
6761                                 tabular.row_info[r].interline_space_default = true;
6762                 else if (value == "none")
6763                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
6764                                 tabular.row_info[r].interline_space_default = false;
6765                                 tabular.row_info[r].interline_space = len;
6766                         }
6767                 else if (isValidLength(value, &len))
6768                         for (row_type r = sel_row_start; r <= sel_row_end; ++r) {
6769                                 tabular.row_info[r].interline_space_default = false;
6770                                 tabular.row_info[r].interline_space = len;
6771                         }
6772                 break;
6773         }
6774
6775         case Tabular::SET_DECIMAL_POINT:
6776                 for (col_type c = sel_col_start; c <= sel_col_end; ++c)
6777                         tabular.column_info[c].decimal_point = from_utf8(value);
6778                 break;
6779
6780         // dummy stuff just to avoid warnings
6781         case Tabular::LAST_ACTION:
6782                 break;
6783         }
6784 }
6785
6786
6787 bool InsetTabular::copySelection(Cursor & cur)
6788 {
6789         if (!cur.selection())
6790                 return false;
6791
6792         row_type rs, re;
6793         col_type cs, ce;
6794         getSelection(cur, rs, re, cs, ce);
6795
6796         paste_tabular.reset(new Tabular(tabular));
6797
6798         for (row_type r = 0; r < rs; ++r)
6799                 paste_tabular->deleteRow(0);
6800
6801         row_type const rows = re - rs + 1;
6802         while (paste_tabular->nrows() > rows)
6803                 paste_tabular->deleteRow(rows);
6804
6805         for (col_type c = 0; c < cs; ++c)
6806                 paste_tabular->deleteColumn(0);
6807
6808         col_type const columns = ce - cs + 1;
6809         while (paste_tabular->ncols() > columns)
6810                 paste_tabular->deleteColumn(columns);
6811
6812         paste_tabular->setBuffer(tabular.buffer());
6813
6814         odocstringstream os;
6815         OutputParams const runparams(0);
6816         paste_tabular->plaintext(os, runparams, 0, true, '\t', INT_MAX);
6817         // Needed for the "Edit->Paste recent" menu and the system clipboard.
6818         cap::copySelection(cur, os.str());
6819
6820         // mark tabular stack dirty
6821         // FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
6822         // when we (hopefully) have a one-for-all paste mechanism.
6823         // This must be called after cap::copySelection.
6824         dirtyTabularStack(true);
6825
6826         return true;
6827 }
6828
6829
6830 bool InsetTabular::pasteClipboard(Cursor & cur)
6831 {
6832         if (!paste_tabular)
6833                 return false;
6834         col_type actcol = tabular.cellColumn(cur.idx());
6835         row_type actrow = tabular.cellRow(cur.idx());
6836
6837         if (cur.selIsMultiCell()) {
6838                 row_type re;
6839                 col_type ce;
6840                 getSelection(cur, actrow, re, actcol, ce);
6841         }
6842
6843         for (row_type r1 = 0, r2 = actrow;
6844              r1 < paste_tabular->nrows() && r2 < tabular.nrows();
6845              ++r1, ++r2) {
6846                 for (col_type c1 = 0, c2 = actcol;
6847                     c1 < paste_tabular->ncols() && c2 < tabular.ncols();
6848                     ++c1, ++c2) {
6849                         if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
6850                               tabular.isPartOfMultiColumn(r2, c2))
6851                                 continue;
6852                         if (paste_tabular->isPartOfMultiColumn(r1, c1)) {
6853                                 --c2;
6854                                 continue;
6855                         }
6856                         if (tabular.isPartOfMultiColumn(r2, c2)) {
6857                                 --c1;
6858                                 continue;
6859                         }
6860                         shared_ptr<InsetTableCell> inset(
6861                                 new InsetTableCell(*paste_tabular->cellInset(r1, c1)));
6862                         tabular.setCellInset(r2, c2, inset);
6863                         // FIXME?: why do we need to do this explicitly? (EL)
6864                         tabular.cellInset(r2, c2)->setBuffer(tabular.buffer());
6865
6866                         // FIXME: change tracking (MG)
6867                         inset->setChange(Change(buffer().params().track_changes ?
6868                                                 Change::INSERTED : Change::UNCHANGED));
6869                         cur.pos() = 0;
6870                         cur.pit() = 0;
6871                 }
6872         }
6873         return true;
6874 }
6875
6876
6877 void InsetTabular::cutSelection(Cursor & cur)
6878 {
6879         if (!cur.selection())
6880                 return;
6881
6882         row_type rs, re;
6883         col_type cs, ce;
6884         getSelection(cur, rs, re, cs, ce);
6885         for (row_type r = rs; r <= re; ++r) {
6886                 for (col_type c = cs; c <= ce; ++c) {
6887                         shared_ptr<InsetTableCell> t
6888                                 = cell(tabular.cellIndex(r, c));
6889                         if (buffer().params().track_changes)
6890                                 // FIXME: Change tracking (MG)
6891                                 t->setChange(Change(Change::DELETED));
6892                         else
6893                                 t->clear();
6894                 }
6895         }
6896
6897         // cursor position might be invalid now
6898         if (cur.pit() > cur.lastpit())
6899                 cur.pit() = cur.lastpit();
6900         if (cur.pos() > cur.lastpos())
6901                 cur.pos() = cur.lastpos();
6902         cur.clearSelection();
6903 }
6904
6905
6906 bool InsetTabular::isRightToLeft(Cursor & cur) const
6907 {
6908         // LASSERT: It might be better to abandon this Buffer.
6909         LASSERT(cur.depth() > 1, return false);
6910         Paragraph const & parentpar = cur[cur.depth() - 2].paragraph();
6911         pos_type const parentpos = cur[cur.depth() - 2].pos();
6912         return parentpar.getFontSettings(buffer().params(),
6913                                          parentpos).language()->rightToLeft();
6914 }
6915
6916
6917 docstring InsetTabular::asString(idx_type stidx, idx_type enidx,
6918                                  bool intoInsets)
6919 {
6920         LASSERT(stidx <= enidx, return docstring());
6921         docstring retval;
6922         col_type const col1 = tabular.cellColumn(stidx);
6923         col_type const col2 = tabular.cellColumn(enidx);
6924         row_type const row1 = tabular.cellRow(stidx);
6925         row_type const row2 = tabular.cellRow(enidx);
6926         bool first = true;
6927         for (col_type col = col1; col <= col2; col++)
6928                 for (row_type row = row1; row <= row2; row++) {
6929                         if (!first)
6930                                 retval += "\n";
6931                         else
6932                                 first = false;
6933                         retval += tabular.cellInset(row, col)->asString(intoInsets);
6934                 }
6935         return retval;
6936 }
6937
6938
6939 void InsetTabular::getSelection(Cursor & cur,
6940         row_type & rs, row_type & re, col_type & cs, col_type & ce) const
6941 {
6942         CursorSlice const & beg = cur.selBegin();
6943         CursorSlice const & end = cur.selEnd();
6944         cs = tabular.cellColumn(beg.idx());
6945         ce = tabular.cellColumn(end.idx());
6946         if (cs > ce)
6947                 swap(cs, ce);
6948
6949         rs = tabular.cellRow(beg.idx());
6950         re = tabular.cellRow(end.idx());
6951         if (rs > re)
6952                 swap(rs, re);
6953 }
6954
6955
6956 Text * InsetTabular::getText(int idx) const
6957 {
6958         return size_t(idx) < nargs() ? cell(idx)->getText(0) : 0;
6959 }
6960
6961
6962 void InsetTabular::setChange(Change const & change)
6963 {
6964         for (idx_type idx = 0; idx < nargs(); ++idx)
6965                 cell(idx)->setChange(change);
6966 }
6967
6968
6969 void InsetTabular::acceptChanges()
6970 {
6971         for (idx_type idx = 0; idx < nargs(); ++idx)
6972                 cell(idx)->acceptChanges();
6973 }
6974
6975
6976 void InsetTabular::rejectChanges()
6977 {
6978         for (idx_type idx = 0; idx < nargs(); ++idx)
6979                 cell(idx)->rejectChanges();
6980 }
6981
6982
6983 bool InsetTabular::allowParagraphCustomization(idx_type cell) const
6984 {
6985         return tabular.getPWidth(cell).zero();
6986 }
6987
6988
6989 bool InsetTabular::forcePlainLayout(idx_type cell) const
6990 {
6991         return tabular.isMultiColumn(cell) && !tabular.getPWidth(cell).zero();
6992 }
6993
6994
6995 bool InsetTabular::insertPlaintextString(BufferView & bv, docstring const & buf,
6996                                      bool usePaste)
6997 {
6998         if (buf.length() <= 0)
6999                 return true;
7000
7001         col_type cols = 1;
7002         row_type rows = 1;
7003         col_type maxCols = 1;
7004         size_t const len = buf.length();
7005         size_t p = 0;
7006
7007         while (p < len &&
7008                (p = buf.find_first_of(from_ascii("\t\n"), p)) != docstring::npos) {
7009                 switch (buf[p]) {
7010                 case '\t':
7011                         ++cols;
7012                         break;
7013                 case '\n':
7014                         if (p + 1 < len)
7015                                 ++rows;
7016                         maxCols = max(cols, maxCols);
7017                         cols = 1;
7018                         break;
7019                 }
7020                 ++p;
7021         }
7022         maxCols = max(cols, maxCols);
7023         Tabular * loctab;
7024         idx_type cell = 0;
7025         col_type ocol = 0;
7026         row_type row = 0;
7027         if (usePaste) {
7028                 paste_tabular.reset(new Tabular(buffer_, rows, maxCols));
7029                 loctab = paste_tabular.get();
7030                 dirtyTabularStack(true);
7031         } else {
7032                 loctab = &tabular;
7033                 cell = bv.cursor().idx();
7034                 ocol = tabular.cellColumn(cell);
7035                 row = tabular.cellRow(cell);
7036         }
7037
7038         size_t op = 0;
7039         idx_type cells = loctab->numberofcells;
7040         p = 0;
7041         cols = ocol;
7042         rows = loctab->nrows();
7043         col_type columns = loctab->ncols();
7044
7045         while (p < len &&
7046                (p = buf.find_first_of(from_ascii("\t\n"), p)) != docstring::npos)
7047         {
7048                 if (p >= len)
7049                         break;
7050                 switch (buf[p]) {
7051                 case '\t':
7052                         // append column if necessary
7053                         if (cols == columns) {
7054                                 loctab->appendColumn(cols - 1);
7055                                 columns = loctab->ncols();
7056                                 cells = loctab->numberofcells;
7057                                 ++cell;
7058                         }
7059                         if (cols < columns) {
7060                                 shared_ptr<InsetTableCell> inset = loctab->cellInset(cell);
7061                                 Font const font = bv.textMetrics(&inset->text()).
7062                                         displayFont(0, 0);
7063                                 inset->setText(buf.substr(op, p - op), font,
7064                                                buffer().params().track_changes);
7065                                 ++cols;
7066                                 ++cell;
7067                         }
7068                         break;
7069                 case '\n':
7070                         // we can only set this if we are not too far right
7071                         if (cols < columns) {
7072                                 shared_ptr<InsetTableCell> inset = tabular.cellInset(cell);
7073                                 Font const font = bv.textMetrics(&inset->text()).
7074                                         displayFont(0, 0);
7075                                 inset->setText(buf.substr(op, p - op), font,
7076                                                buffer().params().track_changes);
7077                         }
7078                         cols = ocol;
7079                         ++row;
7080                         // append row if necessary
7081                         if (row == rows && p < len - 1) {
7082                                 loctab->appendRow(row - 1);
7083                                 rows = loctab->nrows();
7084                                 cells = loctab->numberofcells;
7085                         }
7086                         if (row < rows)
7087                                 cell = loctab->cellIndex(row, cols);
7088                         break;
7089                 }
7090                 ++p;
7091                 op = p;
7092         }
7093         // check for the last cell if there is no trailing '\n'
7094         if (cell < cells && op < len) {
7095                 shared_ptr<InsetTableCell> inset = loctab->cellInset(cell);
7096                 Font const font = bv.textMetrics(&inset->text()).displayFont(0, 0);
7097                 inset->setText(buf.substr(op, len - op), font,
7098                         buffer().params().track_changes);
7099         }
7100         return true;
7101 }
7102
7103
7104 void InsetTabular::addPreview(DocIterator const & inset_pos,
7105         PreviewLoader & loader) const
7106 {
7107         DocIterator cell_pos = inset_pos;
7108
7109         cell_pos.push_back(CursorSlice(*const_cast<InsetTabular *>(this)));
7110         for (row_type r = 0; r < tabular.nrows(); ++r) {
7111                 for (col_type c = 0; c < tabular.ncols(); ++c) {
7112                         cell_pos.top().idx() = tabular.cellIndex(r, c);
7113                         tabular.cellInset(r, c)->addPreview(cell_pos, loader);
7114                 }
7115         }
7116 }
7117
7118
7119 bool InsetTabular::completionSupported(Cursor const & cur) const
7120 {
7121         Cursor const & bvCur = cur.bv().cursor();
7122         if (&bvCur.inset() != this)
7123                 return false;
7124         return cur.text()->completionSupported(cur);
7125 }
7126
7127
7128 bool InsetTabular::inlineCompletionSupported(Cursor const & cur) const
7129 {
7130         return completionSupported(cur);
7131 }
7132
7133
7134 bool InsetTabular::automaticInlineCompletion() const
7135 {
7136         return lyxrc.completion_inline_text;
7137 }
7138
7139
7140 bool InsetTabular::automaticPopupCompletion() const
7141 {
7142         return lyxrc.completion_popup_text;
7143 }
7144
7145
7146 bool InsetTabular::showCompletionCursor() const
7147 {
7148         return lyxrc.completion_cursor_text;
7149 }
7150
7151
7152 CompletionList const * InsetTabular::createCompletionList(Cursor const & cur) const
7153 {
7154         return completionSupported(cur) ? cur.text()->createCompletionList(cur) : 0;
7155 }
7156
7157
7158 docstring InsetTabular::completionPrefix(Cursor const & cur) const
7159 {
7160         if (!completionSupported(cur))
7161                 return docstring();
7162         return cur.text()->completionPrefix(cur);
7163 }
7164
7165
7166 bool InsetTabular::insertCompletion(Cursor & cur, docstring const & s, bool finished)
7167 {
7168         if (!completionSupported(cur))
7169                 return false;
7170
7171         return cur.text()->insertCompletion(cur, s, finished);
7172 }
7173
7174
7175 void InsetTabular::completionPosAndDim(Cursor const & cur, int & x, int & y,
7176                                     Dimension & dim) const
7177 {
7178         TextMetrics const & tm = cur.bv().textMetrics(cur.text());
7179         tm.completionPosAndDim(cur, x, y, dim);
7180 }
7181
7182
7183 void InsetTabular::string2params(string const & in, InsetTabular & inset)
7184 {
7185         istringstream data(in);
7186         Lexer lex;
7187         lex.setStream(data);
7188
7189         if (in.empty())
7190                 return;
7191
7192         string token;
7193         lex >> token;
7194         if (!lex || token != "tabular") {
7195                 LYXERR0("Expected arg 1 to be \"tabular\" in " << in);
7196                 return;
7197         }
7198
7199         // This is part of the inset proper that is usually swallowed
7200         // by Buffer::readInset
7201         lex >> token;
7202         if (!lex || token != "Tabular") {
7203                 LYXERR0("Expected arg 2 to be \"Tabular\" in " << in);
7204                 return;
7205         }
7206
7207         inset.read(lex);
7208 }
7209
7210
7211 string InsetTabular::params2string(InsetTabular const & inset)
7212 {
7213         ostringstream data;
7214         data << "tabular" << ' ';
7215         inset.write(data);
7216         data << "\\end_inset\n";
7217         return data.str();
7218 }
7219
7220
7221 void InsetTabular::setLayoutForHiddenCells(DocumentClass const & dc)
7222 {
7223         for (Tabular::col_type c = 0; c < tabular.ncols(); ++c) {
7224                 for (Tabular::row_type r = 0; r < tabular.nrows(); ++r) {
7225                         if (!tabular.isPartOfMultiColumn(r,c) &&
7226                             !tabular.isPartOfMultiRow(r,c))
7227                                 continue;
7228
7229                         ParagraphList & parlist = tabular.cellInset(r,c)->paragraphs();
7230                         ParagraphList::iterator it = parlist.begin();
7231                         ParagraphList::iterator const en = parlist.end();
7232                         for (; it != en; ++it)
7233                                         it->setLayout(dc.plainLayout());
7234                 }
7235         }
7236 }
7237
7238
7239 } // namespace lyx