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