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