]> git.lyx.org Git - lyx.git/blob - src/insets/InsetTabular.cpp
'using namespace lyx::support' instead of 'using support::xxx'
[lyx.git] / src / insets / InsetTabular.cpp
1 /**
2  * \file InsetTabular.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  * \author Lars Gullik Bjønnes
8  * \author Matthias Ettrich
9  * \author José Matos
10  * \author Jean-Marc Lasgouttes
11  * \author Angus Leeming
12  * \author John Levon
13  * \author André Pönitz
14  * \author Jürgen Vigna
15  *
16  * Full author contact details are available in file CREDITS.
17  */
18
19 #include <config.h>
20
21 #include "InsetTabular.h"
22
23 #include "buffer_funcs.h"
24 #include "Buffer.h"
25 #include "BufferParams.h"
26 #include "BufferView.h"
27 #include "CoordCache.h"
28 #include "Counters.h"
29 #include "Cursor.h"
30 #include "CutAndPaste.h"
31 #include "support/debug.h"
32 #include "DispatchResult.h"
33 #include "FuncRequest.h"
34 #include "FuncStatus.h"
35 #include "support/gettext.h"
36 #include "Language.h"
37 #include "LaTeXFeatures.h"
38 #include "Lexer.h"
39 #include "LyXFunc.h"
40 #include "MetricsInfo.h"
41 #include "OutputParams.h"
42 #include "paragraph_funcs.h"
43 #include "Paragraph.h"
44 #include "ParagraphParameters.h"
45 #include "ParIterator.h"
46 #include "TextClass.h"
47 #include "TextMetrics.h"
48
49 #include "support/convert.h"
50 #include "support/docstream.h"
51 #include "support/FileName.h"
52 #include "support/lstrings.h"
53
54 #include "frontends/alert.h"
55 #include "frontends/Clipboard.h"
56 #include "frontends/Painter.h"
57 #include "frontends/Selection.h"
58
59 #include <boost/scoped_ptr.hpp>
60
61 #include <sstream>
62 #include <iostream>
63 #include <limits>
64
65 using namespace std;
66 using namespace lyx::support;
67
68 using boost::shared_ptr;
69 using boost::dynamic_pointer_cast;
70
71 namespace lyx {
72
73 using cap::dirtyTabularStack;
74 using cap::tabularStackDirty;
75
76 using graphics::PreviewLoader;
77
78 using frontend::Painter;
79 using frontend::Clipboard;
80
81 namespace Alert = frontend::Alert;
82
83
84 namespace {
85
86 int const ADD_TO_HEIGHT = 2;
87 int const ADD_TO_TABULAR_WIDTH = 2;
88 int const default_line_space = 10;
89 int const WIDTH_OF_LINE = 5;
90
91
92 ///
93 boost::scoped_ptr<Tabular> paste_tabular;
94
95
96 struct TabularFeature {
97         Tabular::Feature action;
98         string feature;
99 };
100
101
102 TabularFeature tabularFeature[] =
103 {
104         { Tabular::APPEND_ROW, "append-row" },
105         { Tabular::APPEND_COLUMN, "append-column" },
106         { Tabular::DELETE_ROW, "delete-row" },
107         { Tabular::DELETE_COLUMN, "delete-column" },
108         { Tabular::COPY_ROW, "copy-row" },
109         { Tabular::COPY_COLUMN, "copy-column" },
110         { Tabular::TOGGLE_LINE_TOP, "toggle-line-top" },
111         { Tabular::TOGGLE_LINE_BOTTOM, "toggle-line-bottom" },
112         { Tabular::TOGGLE_LINE_LEFT, "toggle-line-left" },
113         { Tabular::TOGGLE_LINE_RIGHT, "toggle-line-right" },
114         { Tabular::ALIGN_LEFT, "align-left" },
115         { Tabular::ALIGN_RIGHT, "align-right" },
116         { Tabular::ALIGN_CENTER, "align-center" },
117         { Tabular::ALIGN_BLOCK, "align-block" },
118         { Tabular::VALIGN_TOP, "valign-top" },
119         { Tabular::VALIGN_BOTTOM, "valign-bottom" },
120         { Tabular::VALIGN_MIDDLE, "valign-middle" },
121         { Tabular::M_TOGGLE_LINE_TOP, "m-toggle-line-top" },
122         { Tabular::M_TOGGLE_LINE_BOTTOM, "m-toggle-line-bottom" },
123         { Tabular::M_TOGGLE_LINE_LEFT, "m-toggle-line-left" },
124         { Tabular::M_TOGGLE_LINE_RIGHT, "m-toggle-line-right" },
125         { Tabular::M_ALIGN_LEFT, "m-align-left" },
126         { Tabular::M_ALIGN_RIGHT, "m-align-right" },
127         { Tabular::M_ALIGN_CENTER, "m-align-center" },
128         { Tabular::M_VALIGN_TOP, "m-valign-top" },
129         { Tabular::M_VALIGN_BOTTOM, "m-valign-bottom" },
130         { Tabular::M_VALIGN_MIDDLE, "m-valign-middle" },
131         { Tabular::MULTICOLUMN, "multicolumn" },
132         { Tabular::SET_ALL_LINES, "set-all-lines" },
133         { Tabular::UNSET_ALL_LINES, "unset-all-lines" },
134         { Tabular::SET_LONGTABULAR, "set-longtabular" },
135         { Tabular::UNSET_LONGTABULAR, "unset-longtabular" },
136         { Tabular::SET_PWIDTH, "set-pwidth" },
137         { Tabular::SET_MPWIDTH, "set-mpwidth" },
138         { Tabular::SET_ROTATE_TABULAR, "set-rotate-tabular" },
139         { Tabular::UNSET_ROTATE_TABULAR, "unset-rotate-tabular" },
140         { Tabular::TOGGLE_ROTATE_TABULAR, "toggle-rotate-tabular" },
141         { Tabular::SET_ROTATE_CELL, "set-rotate-cell" },
142         { Tabular::UNSET_ROTATE_CELL, "unset-rotate-cell" },
143         { Tabular::TOGGLE_ROTATE_CELL, "toggle-rotate-cell" },
144         { Tabular::SET_USEBOX, "set-usebox" },
145         { Tabular::SET_LTHEAD, "set-lthead" },
146         { Tabular::UNSET_LTHEAD, "unset-lthead" },
147         { Tabular::SET_LTFIRSTHEAD, "set-ltfirsthead" },
148         { Tabular::UNSET_LTFIRSTHEAD, "unset-ltfirsthead" },
149         { Tabular::SET_LTFOOT, "set-ltfoot" },
150         { Tabular::UNSET_LTFOOT, "unset-ltfoot" },
151         { Tabular::SET_LTLASTFOOT, "set-ltlastfoot" },
152         { Tabular::UNSET_LTLASTFOOT, "unset-ltlastfoot" },
153         { Tabular::SET_LTNEWPAGE, "set-ltnewpage" },
154         { Tabular::SET_SPECIAL_COLUMN, "set-special-column" },
155         { Tabular::SET_SPECIAL_MULTI, "set-special-multi" },
156         { Tabular::SET_BOOKTABS, "set-booktabs" },
157         { Tabular::UNSET_BOOKTABS, "unset-booktabs" },
158         { Tabular::SET_TOP_SPACE, "set-top-space" },
159         { Tabular::SET_BOTTOM_SPACE, "set-bottom-space" },
160         { Tabular::SET_INTERLINE_SPACE, "set-interline-space" },
161         { Tabular::LAST_ACTION, "" }
162 };
163
164
165 class FeatureEqual : public std::unary_function<TabularFeature, bool> {
166 public:
167         FeatureEqual(Tabular::Feature feature)
168                 : feature_(feature) {}
169         bool operator()(TabularFeature const & tf) const {
170                 return tf.action == feature_;
171         }
172 private:
173         Tabular::Feature feature_;
174 };
175
176
177 template <class T>
178 string const write_attribute(string const & name, T const & t)
179 {
180         string const s = tostr(t);
181         return s.empty() ? s : " " + name + "=\"" + s + "\"";
182 }
183
184 template <>
185 string const write_attribute(string const & name, string const & t)
186 {
187         return t.empty() ? t : " " + name + "=\"" + t + "\"";
188 }
189
190
191 template <>
192 string const write_attribute(string const & name, docstring const & t)
193 {
194         return t.empty() ? string() : " " + name + "=\"" + to_utf8(t) + "\"";
195 }
196
197
198 template <>
199 string const write_attribute(string const & name, bool const & b)
200 {
201         // we write only true attribute values so we remove a bit of the
202         // file format bloat for tabulars.
203         return b ? write_attribute(name, convert<string>(b)) : string();
204 }
205
206
207 template <>
208 string const write_attribute(string const & name, int const & i)
209 {
210         // we write only true attribute values so we remove a bit of the
211         // file format bloat for tabulars.
212         return i ? write_attribute(name, convert<string>(i)) : string();
213 }
214
215
216 template <>
217 string const write_attribute(string const & name, Tabular::idx_type const & i)
218 {
219         // we write only true attribute values so we remove a bit of the
220         // file format bloat for tabulars.
221         return i ? write_attribute(name, convert<string>(i)) : string();
222 }
223
224
225 template <>
226 string const write_attribute(string const & name, Length const & value)
227 {
228         // we write only the value if we really have one same reson as above.
229         return value.zero() ? string() : write_attribute(name, value.asString());
230 }
231
232
233 string const tostr(LyXAlignment const & num)
234 {
235         switch (num) {
236         case LYX_ALIGN_NONE:
237                 return "none";
238         case LYX_ALIGN_BLOCK:
239                 return "block";
240         case LYX_ALIGN_LEFT:
241                 return "left";
242         case LYX_ALIGN_CENTER:
243                 return "center";
244         case LYX_ALIGN_RIGHT:
245                 return "right";
246         case LYX_ALIGN_LAYOUT:
247                 return "layout";
248         case LYX_ALIGN_SPECIAL:
249                 return "special";
250         }
251         return string();
252 }
253
254
255 string const tostr(Tabular::VAlignment const & num)
256 {
257         switch (num) {
258         case Tabular::LYX_VALIGN_TOP:
259                 return "top";
260         case Tabular::LYX_VALIGN_MIDDLE:
261                 return "middle";
262         case Tabular::LYX_VALIGN_BOTTOM:
263                 return "bottom";
264         }
265         return string();
266 }
267
268
269 string const tostr(Tabular::BoxType const & num)
270 {
271         switch (num) {
272         case Tabular::BOX_NONE:
273                 return "none";
274         case Tabular::BOX_PARBOX:
275                 return "parbox";
276         case Tabular::BOX_MINIPAGE:
277                 return "minipage";
278         }
279         return string();
280 }
281
282
283 // I would have liked a fromstr template a lot better. (Lgb)
284 bool string2type(string const str, LyXAlignment & num)
285 {
286         if (str == "none")
287                 num = LYX_ALIGN_NONE;
288         else if (str == "block")
289                 num = LYX_ALIGN_BLOCK;
290         else if (str == "left")
291                 num = LYX_ALIGN_LEFT;
292         else if (str == "center")
293                 num = LYX_ALIGN_CENTER;
294         else if (str == "right")
295                 num = LYX_ALIGN_RIGHT;
296         else
297                 return false;
298         return true;
299 }
300
301
302 bool string2type(string const str, Tabular::VAlignment & num)
303 {
304         if (str == "top")
305                 num = Tabular::LYX_VALIGN_TOP;
306         else if (str == "middle" )
307                 num = Tabular::LYX_VALIGN_MIDDLE;
308         else if (str == "bottom")
309                 num = Tabular::LYX_VALIGN_BOTTOM;
310         else
311                 return false;
312         return true;
313 }
314
315
316 bool string2type(string const str, Tabular::BoxType & num)
317 {
318         if (str == "none")
319                 num = Tabular::BOX_NONE;
320         else if (str == "parbox")
321                 num = Tabular::BOX_PARBOX;
322         else if (str == "minipage")
323                 num = Tabular::BOX_MINIPAGE;
324         else
325                 return false;
326         return true;
327 }
328
329
330 bool string2type(string const str, bool & num)
331 {
332         if (str == "true")
333                 num = true;
334         else if (str == "false")
335                 num = false;
336         else
337                 return false;
338         return true;
339 }
340
341
342 bool getTokenValue(string const & str, char const * token, string & ret)
343 {
344         ret.erase();
345         size_t token_length = strlen(token);
346         string::size_type pos = str.find(token);
347
348         if (pos == string::npos || pos + token_length + 1 >= str.length()
349                 || str[pos + token_length] != '=')
350                 return false;
351         pos += token_length + 1;
352         char ch = str[pos];
353         if (ch != '"' && ch != '\'') { // only read till next space
354                 ret += ch;
355                 ch = ' ';
356         }
357         while (pos < str.length() - 1 && str[++pos] != ch)
358                 ret += str[pos];
359
360         return true;
361 }
362
363
364 bool getTokenValue(string const & str, char const * token, docstring & ret)
365 {
366         string tmp;
367         bool const success = getTokenValue(str, token, tmp);
368         ret = from_utf8(tmp);
369         return success;
370 }
371
372
373 bool getTokenValue(string const & str, char const * token, int & num)
374 {
375         string tmp;
376         num = 0;
377         if (!getTokenValue(str, token, tmp))
378                 return false;
379         num = convert<int>(tmp);
380         return true;
381 }
382
383
384 bool getTokenValue(string const & str, char const * token, LyXAlignment & num)
385 {
386         string tmp;
387         return getTokenValue(str, token, tmp) && string2type(tmp, num);
388 }
389
390
391 bool getTokenValue(string const & str, char const * token,
392                                    Tabular::VAlignment & num)
393 {
394         string tmp;
395         return getTokenValue(str, token, tmp) && string2type(tmp, num);
396 }
397
398
399 bool getTokenValue(string const & str, char const * token,
400                                    Tabular::BoxType & num)
401 {
402         string tmp;
403         return getTokenValue(str, token, tmp) && string2type(tmp, num);
404 }
405
406
407 bool getTokenValue(string const & str, char const * token, bool & flag)
408 {
409         // set the flag always to false as this should be the default for bools
410         // not in the file-format.
411         flag = false;
412         string tmp;
413         return getTokenValue(str, token, tmp) && string2type(tmp, flag);
414 }
415
416
417 bool getTokenValue(string const & str, char const * token, Length & len)
418 {
419         // set the length to be zero() as default as this it should be if not
420         // in the file format.
421         len = Length();
422         string tmp;
423         return getTokenValue(str, token, tmp) && isValidLength(tmp, &len);
424 }
425
426
427 bool getTokenValue(string const & str, char const * token, Length & len, bool & flag)
428 {
429         len = Length();
430         flag = false;
431         string tmp;
432         if (!getTokenValue(str, token, tmp))
433                 return false;
434         if (tmp == "default") {
435                 flag = true;
436                 return  true;
437         }
438         return isValidLength(tmp, &len);
439 }
440
441
442 void l_getline(istream & is, string & str)
443 {
444         str.erase();
445         while (str.empty()) {
446                 getline(is, str);
447                 if (!str.empty() && str[str.length() - 1] == '\r')
448                         str.erase(str.length() - 1);
449         }
450 }
451
452 } // namespace
453
454
455 string const featureAsString(Tabular::Feature feature)
456 {
457         TabularFeature * end = tabularFeature +
458                 sizeof(tabularFeature) / sizeof(TabularFeature);
459         TabularFeature * it = std::find_if(tabularFeature, end,
460                                            FeatureEqual(feature));
461         return (it == end) ? string() : it->feature;
462 }
463
464
465
466 /////////////////////////////////////////////////////////////////////
467 //
468 // Tabular
469 //
470 /////////////////////////////////////////////////////////////////////
471
472
473 Tabular::cellstruct::cellstruct(BufferParams const & bp)
474         : cellno(0),
475           width(0),
476           multicolumn(Tabular::CELL_NORMAL),
477           alignment(LYX_ALIGN_CENTER),
478           valignment(LYX_VALIGN_TOP),
479           top_line(true),
480           bottom_line(false),
481           left_line(true),
482           right_line(false),
483           usebox(BOX_NONE),
484           rotate(false),
485           inset(new InsetText(bp))
486 {}
487
488
489 Tabular::cellstruct::cellstruct(cellstruct const & cs)
490         : cellno(cs.cellno),
491           width(cs.width),
492           multicolumn(cs.multicolumn),
493           alignment(cs.alignment),
494           valignment(cs.valignment),
495           top_line(cs.top_line),
496           bottom_line(cs.bottom_line),
497           left_line(cs.left_line),
498           right_line(cs.right_line),
499           usebox(cs.usebox),
500           rotate(cs.rotate),
501           align_special(cs.align_special),
502           p_width(cs.p_width),
503           inset(dynamic_cast<InsetText*>(cs.inset->clone()))
504 {}
505
506
507 Tabular::cellstruct & Tabular::cellstruct::operator=(cellstruct cs)
508 {
509         swap(cs);
510         return *this;
511 }
512
513
514 void Tabular::cellstruct::swap(cellstruct & rhs)
515 {
516         std::swap(cellno, rhs.cellno);
517         std::swap(width, rhs.width);
518         std::swap(multicolumn, rhs.multicolumn);
519         std::swap(alignment, rhs.alignment);
520         std::swap(valignment, rhs.valignment);
521         std::swap(top_line, rhs.top_line);
522         std::swap(bottom_line, rhs.bottom_line);
523         std::swap(left_line, rhs.left_line);
524         std::swap(right_line, rhs.right_line);
525         std::swap(usebox, rhs.usebox);
526         std::swap(rotate, rhs.rotate);
527         std::swap(align_special, rhs.align_special);
528         p_width.swap(rhs.p_width);
529         inset.swap(rhs.inset);
530 }
531
532
533 Tabular::rowstruct::rowstruct()
534         : ascent(0),
535           descent(0),
536           top_line(true),
537           bottom_line(false),
538           top_space_default(false),
539           bottom_space_default(false),
540           interline_space_default(false),
541           endhead(false),
542           endfirsthead(false),
543           endfoot(false),
544           endlastfoot(false),
545           newpage(false)
546 {}
547
548
549 Tabular::columnstruct::columnstruct()
550         : alignment(LYX_ALIGN_CENTER),
551           valignment(LYX_VALIGN_TOP),
552           left_line(true),
553           right_line(false),
554           width(0)
555 {
556 }
557
558
559 Tabular::ltType::ltType()
560         : topDL(false),
561           bottomDL(false),
562           empty(false)
563 {}
564
565
566 Tabular::Tabular()
567 {
568         // unusable now!
569 }
570
571
572 Tabular::Tabular(BufferParams const & bp, row_type rows_arg,
573                        col_type columns_arg)
574 {
575         init(bp, rows_arg, columns_arg);
576 }
577
578
579 // activates all lines and sets all widths to 0
580 void Tabular::init(BufferParams const & bp, row_type rows_arg,
581                       col_type columns_arg)
582 {
583         row_info = row_vector(rows_arg);
584         column_info = column_vector(columns_arg);
585         cell_info = cell_vvector(rows_arg, cell_vector(columns_arg, cellstruct(bp)));
586         row_info.reserve(10);
587         column_info.reserve(10);
588         cell_info.reserve(100);
589         fixCellNums();
590         for (row_type i = 0; i < rows_arg; ++i)
591                 cell_info[i].back().right_line = true;
592         row_info.back().bottom_line = true;
593         row_info.front().bottom_line = true;
594         column_info.back().right_line = true;
595         is_long_tabular = false;
596         rotate = false;
597         use_booktabs = false;
598 }
599
600
601 void Tabular::fixCellNums()
602 {
603         idx_type cellno = 0;
604         for (row_type i = 0; i < rowCount(); ++i) {
605                 for (col_type j = 0; j < columnCount(); ++j) {
606                         // When debugging it can be nice to set
607                         // this to true.
608                         cell_info[i][j].inset->setDrawFrame(false);
609                         cell_info[i][j].cellno = cellno++;
610                 }
611                 cell_info[i].back().right_line = true;
612         }
613
614         updateIndexes();
615 }
616
617
618 void Tabular::appendRow(BufferParams const & bp, idx_type const cell)
619 {
620         row_type const row = cellRow(cell);
621
622         row_vector::iterator rit = row_info.begin() + row;
623         row_info.insert(rit, rowstruct());
624         // now set the values of the row before
625         row_info[row] = row_info[row + 1];
626
627         row_type const nrows = rowCount();
628         col_type const ncols = columnCount();
629
630         cell_vvector old(nrows - 1);
631         for (row_type i = 0; i < nrows - 1; ++i)
632                 swap(cell_info[i], old[i]);
633
634         cell_info = cell_vvector(nrows, cell_vector(ncols, cellstruct(bp)));
635
636         for (row_type i = 0; i <= row; ++i)
637                 swap(cell_info[i], old[i]);
638         for (row_type i = row + 2; i < nrows; ++i)
639                 swap(cell_info[i], old[i - 1]);
640
641         if (bp.trackChanges)
642                 for (col_type j = 0; j < ncols; ++j)
643                         cell_info[row + 1][j].inset->setChange(Change(Change::INSERTED));
644
645         updateIndexes();
646 }
647
648
649 void Tabular::deleteRow(row_type const row)
650 {
651         // Not allowed to delete last row
652         if (rowCount() == 1)
653                 return;
654
655         row_info.erase(row_info.begin() + row);
656         cell_info.erase(cell_info.begin() + row);
657         fixCellNums();
658 }
659
660
661 void Tabular::copyRow(BufferParams const & bp, row_type const row)
662 {
663         row_info.insert(row_info.begin() + row, row_info[row]);
664         cell_info.insert(cell_info.begin() + row, cell_info[row]);
665
666         if (bp.trackChanges)
667                 for (col_type j = 0; j < columnCount(); ++j)
668                         cell_info[row + 1][j].inset->setChange(Change(Change::INSERTED));
669
670         updateIndexes();
671 }
672
673
674 void Tabular::appendColumn(BufferParams const & bp, idx_type const cell)
675 {
676         col_type const column = cellColumn(cell);
677         col_type const ncols = columnCount();
678         column_vector::iterator cit = column_info.begin() + column + 1;
679         column_info.insert(cit, columnstruct());
680         // set the column values of the column before
681         column_info[column + 1] = column_info[column];
682
683         for (row_type i = 0; i < rowCount(); ++i) {
684                 cell_info[i].insert(cell_info[i].begin() + column + 1, cellstruct(bp));
685
686                 // care about multicolumns
687                 if (cell_info[i][column + 1].multicolumn == CELL_BEGIN_OF_MULTICOLUMN)
688                         cell_info[i][column + 1].multicolumn = CELL_PART_OF_MULTICOLUMN;
689
690                 if (column + 2 >= ncols
691                     || cell_info[i][column + 2].multicolumn != CELL_PART_OF_MULTICOLUMN)
692                         cell_info[i][column + 1].multicolumn = Tabular::CELL_NORMAL;
693         }
694         //++column;
695         for (row_type i = 0; i < rowCount(); ++i) {
696                 cell_info[i][column + 1].inset->clear();
697                 if (bp.trackChanges)
698                         cell_info[i][column + 1].inset->setChange(Change(Change::INSERTED));
699         }
700         fixCellNums();
701 }
702
703
704 void Tabular::deleteColumn(col_type const column)
705 {
706         // Not allowed to delete last column
707         if (columnCount() == 1)
708                 return;
709
710         column_info.erase(column_info.begin() + column);
711         for (row_type i = 0; i < rowCount(); ++i) {
712                 // Care about multicolumn cells
713                 if (column + 1 < columnCount() &&
714                     cell_info[i][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN &&
715                     cell_info[i][column + 1].multicolumn == CELL_PART_OF_MULTICOLUMN) {
716                         cell_info[i][column + 1].multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
717                 }
718                 cell_info[i].erase(cell_info[i].begin() + column);
719         }
720         fixCellNums();
721 }
722
723
724 void Tabular::copyColumn(BufferParams const & bp, col_type const column)
725 {
726         column_info.insert(column_info.begin() + column, column_info[column]);
727
728         for (row_type i = 0; i < rowCount(); ++i)
729                 cell_info[i].insert(cell_info[i].begin() + column, cell_info[i][column]);
730
731         if (bp.trackChanges)
732                 for (row_type i = 0; i < rowCount(); ++i)
733                         cell_info[i][column + 1].inset->setChange(Change(Change::INSERTED));
734         fixCellNums();
735 }
736
737
738 void Tabular::updateIndexes()
739 {
740         numberofcells = 0;
741         // Count only non-multicol cells plus begin multicol
742         // cells, ignore non-begin multicol cells:
743         for (row_type row = 0; row < rowCount(); ++row) {
744                 for (col_type column = 0; column < columnCount(); ++column) {
745                         if (cell_info[row][column].multicolumn
746                                 != Tabular::CELL_PART_OF_MULTICOLUMN)
747                                 ++numberofcells;
748                         BOOST_ASSERT(numberofcells != 0);
749                         cell_info[row][column].cellno =
750                                 numberofcells - 1;
751                 }
752         }
753
754         rowofcell.resize(numberofcells);
755         columnofcell.resize(numberofcells);
756
757         row_type row = 0;
758         col_type column = 0;
759         for (idx_type c = 0;
760                  c < numberofcells && row < rowCount() && column < columnCount();) {
761                 rowofcell[c] = row;
762                 columnofcell[c] = column;
763                 ++c;
764                 do {
765                         ++column;
766                 } while (column < columnCount() &&
767                                  cell_info[row][column].multicolumn
768                                  == Tabular::CELL_PART_OF_MULTICOLUMN);
769
770                 if (column == columnCount()) {
771                         column = 0;
772                         ++row;
773                 }
774         }
775
776         for (row_type row = 0; row < rowCount(); ++row) {
777                 for (col_type column = 0; column < columnCount(); ++column) {
778                         if (isPartOfMultiColumn(row,column))
779                                 continue;
780                         cell_info[row][column].inset->setAutoBreakRows(
781                                 !getPWidth(cellIndex(row, column)).zero());
782                 }
783         }
784 }
785
786
787 Tabular::idx_type Tabular::cellCount() const
788 {
789         return numberofcells;
790 }
791
792
793 Tabular::idx_type Tabular::numberOfCellsInRow(idx_type const cell) const
794 {
795         row_type const row = cellRow(cell);
796         idx_type result = 0;
797         for (col_type i = 0; i < columnCount(); ++i)
798                 if (cell_info[row][i].multicolumn != Tabular::CELL_PART_OF_MULTICOLUMN)
799                         ++result;
800         return result;
801 }
802
803
804 bool Tabular::topLine(idx_type const cell, bool const wholerow) const
805 {
806         if (!wholerow && isMultiColumn(cell) &&
807             !(use_booktabs && cellRow(cell) == 0))
808                 return cellinfo_of_cell(cell).top_line;
809         return row_info[cellRow(cell)].top_line;
810 }
811
812
813 bool Tabular::bottomLine(idx_type const cell, bool wholerow) const
814 {
815         if (!wholerow && isMultiColumn(cell) &&
816             !(use_booktabs && isLastRow(cell)))
817                 return cellinfo_of_cell(cell).bottom_line;
818         return row_info[cellRow(cell)].bottom_line;
819 }
820
821
822 bool Tabular::leftLine(idx_type cell, bool wholecolumn) const
823 {
824         if (use_booktabs)
825                 return false;
826         if (!wholecolumn && isMultiColumn(cell) &&
827                 (isFirstCellInRow(cell) || isMultiColumn(cell-1)))
828                 return cellinfo_of_cell(cell).left_line;
829         return column_info[cellColumn(cell)].left_line;
830 }
831
832
833 bool Tabular::rightLine(idx_type cell, bool wholecolumn) const
834 {
835         if (use_booktabs)
836                 return false;
837         if (!wholecolumn && isMultiColumn(cell) &&
838                 (isLastCellInRow(cell) || isMultiColumn(cell + 1)))
839                 return cellinfo_of_cell(cell).right_line;
840         return column_info[cellRightColumn(cell)].right_line;
841 }
842
843
844 bool Tabular::topAlreadyDrawn(idx_type cell) const
845 {
846         row_type row = cellRow(cell);
847         if (row > 0 && !getAdditionalHeight(row)) {
848                 col_type column = cellColumn(cell);
849                 --row;
850                 while (column
851                            && cell_info[row][column].multicolumn
852                            == Tabular::CELL_PART_OF_MULTICOLUMN)
853                         --column;
854                 if (cell_info[row][column].multicolumn == Tabular::CELL_NORMAL)
855                         return row_info[row].bottom_line;
856                 else
857                         return cell_info[row][column].bottom_line;
858         }
859         return false;
860 }
861
862
863 bool Tabular::leftAlreadyDrawn(idx_type cell) const
864 {
865         col_type column = cellColumn(cell);
866         if (column > 0) {
867                 row_type row = cellRow(cell);
868                 while (--column &&
869                            (cell_info[row][column].multicolumn ==
870                                 Tabular::CELL_PART_OF_MULTICOLUMN)) { }
871                 if (getAdditionalWidth(cell_info[row][column].cellno))
872                         return false;
873                 return rightLine(cell_info[row][column].cellno);
874         }
875         return false;
876 }
877
878
879 bool Tabular::isLastRow(idx_type cell) const
880 {
881         return cellRow(cell) == rowCount() - 1;
882 }
883
884
885 int Tabular::getAdditionalHeight(row_type row) const
886 {
887         if (!row || row >= rowCount())
888                 return 0;
889
890         bool top = true;
891         bool bottom = true;
892
893         for (col_type column = 0; column < columnCount() && bottom; ++column) {
894                 switch (cell_info[row - 1][column].multicolumn) {
895                 case Tabular::CELL_BEGIN_OF_MULTICOLUMN:
896                         bottom = cell_info[row - 1][column].bottom_line;
897                         break;
898                 case Tabular::CELL_NORMAL:
899                         bottom = row_info[row - 1].bottom_line;
900                 }
901         }
902         for (col_type column = 0; column < columnCount() && top; ++column) {
903                 switch (cell_info[row][column].multicolumn) {
904                 case Tabular::CELL_BEGIN_OF_MULTICOLUMN:
905                         top = cell_info[row][column].top_line;
906                         break;
907                 case Tabular::CELL_NORMAL:
908                         top = row_info[row].top_line;
909                 }
910         }
911         int const interline_space = row_info[row - 1].interline_space_default ?
912                 default_line_space :
913                 row_info[row - 1].interline_space.inPixels(width());
914         if (top && bottom)
915                 return interline_space + WIDTH_OF_LINE;
916         return interline_space;
917 }
918
919
920 int Tabular::getAdditionalWidth(idx_type cell) const
921 {
922         // internally already set in setCellWidth
923         // used to get it back in text.cpp
924         col_type const col = cellRightColumn(cell);
925         row_type const row = cellRow(cell);
926         if (col < columnCount() - 1 && rightLine(cell) &&
927                 leftLine(cell_info[row][col+1].cellno)) // column_info[col+1].left_line)
928         {
929                 return WIDTH_OF_LINE;
930         }
931         return 0;
932 }
933
934
935 // returns the maximum over all rows
936 int Tabular::columnWidth(idx_type cell) const
937 {
938         col_type const column1 = cellColumn(cell);
939         col_type const column2 = cellRightColumn(cell);
940         int result = 0;
941         for (col_type i = column1; i <= column2; ++i)
942                 result += column_info[i].width;
943         return result;
944 }
945
946
947 int Tabular::width() const
948 {
949         int width = 0;
950         for (col_type i = 0; i < columnCount(); ++i)
951                 width += column_info[i].width;
952         return width;
953 }
954
955
956 // returns true if a complete update is necessary, otherwise false
957 bool Tabular::setWidthOfMulticolCell(idx_type cell, int new_width)
958 {
959         if (!isMultiColumn(cell))
960                 return false;
961
962         row_type const row = cellRow(cell);
963         col_type const column1 = cellColumn(cell);
964         col_type const column2 = cellRightColumn(cell);
965         int const old_val = cell_info[row][column2].width;
966
967         // first set columns to 0 so we can calculate the right width
968         for (col_type i = column1; i <= column2; ++i) {
969                 cell_info[row][i].width = 0;
970         }
971         // set the width to MAX_WIDTH until width > 0
972         int width = new_width + 2 * WIDTH_OF_LINE;
973         col_type i = column1;
974         for (; i < column2 && width > column_info[i].width; ++i) {
975                 cell_info[row][i].width = column_info[i].width;
976                 width -= column_info[i].width;
977         }
978         if (width > 0) {
979                 cell_info[row][i].width = width;
980         }
981         if (old_val != cell_info[row][column2].width) {
982                 // in this case we have to recalculate all multicolumn cells which
983                 // have this column as one of theirs but not as last one
984                 calculate_width_of_column_NMC(i);
985                 recalculateMulticolumnsOfColumn(i);
986                 calculate_width_of_column(i);
987         }
988         return true;
989 }
990
991
992 void Tabular::recalculateMulticolumnsOfColumn(col_type column)
993 {
994         // the last column does not have to be recalculated because all
995         // multicolumns will have here there last multicolumn cell which
996         // always will have the whole rest of the width of the cell.
997         if (columnCount() < 2 || column > (columnCount() - 2))
998                 return;
999         for (row_type row = 0; row < rowCount(); ++row) {
1000                 int mc = cell_info[row][column].multicolumn;
1001                 int nmc = cell_info[row][column+1].multicolumn;
1002                 // we only have to update multicolumns which do not have this
1003                 // column as their last column!
1004                 if (mc == CELL_BEGIN_OF_MULTICOLUMN ||
1005                           (mc == CELL_PART_OF_MULTICOLUMN &&
1006                            nmc == CELL_PART_OF_MULTICOLUMN))
1007                 {
1008                         idx_type const cellno = cell_info[row][column].cellno;
1009                         setWidthOfMulticolCell(cellno,
1010                                                cellWidth(cellno) - 2 * WIDTH_OF_LINE);
1011                 }
1012         }
1013 }
1014
1015
1016 void Tabular::setCellWidth(idx_type cell, int new_width)
1017 {
1018         row_type const row = cellRow(cell);
1019         col_type const column1 = cellColumn(cell);
1020         bool tmp = false;
1021         int width = 0;
1022         int add_width = 0;
1023
1024         if (rightLine(cell_info[row][column1].cellno, true) &&
1025                 column1 < columnCount() - 1 &&
1026                 leftLine(cell_info[row][column1+1].cellno, true))
1027         {
1028                 add_width = WIDTH_OF_LINE;
1029         }
1030
1031         if (cellWidth(cell) == new_width + 2 * WIDTH_OF_LINE + add_width)
1032                 return;
1033
1034         if (isMultiColumnReal(cell)) {
1035                 tmp = setWidthOfMulticolCell(cell, new_width);
1036         } else {
1037                 width = new_width + 2 * WIDTH_OF_LINE + add_width;
1038                 cell_info[row][column1].width = width;
1039                 tmp = calculate_width_of_column_NMC(column1);
1040                 if (tmp)
1041                         recalculateMulticolumnsOfColumn(column1);
1042         }
1043         if (tmp) {
1044                 for (col_type i = 0; i < columnCount(); ++i)
1045                         calculate_width_of_column(i);
1046         }
1047 }
1048
1049
1050 void Tabular::setAlignment(idx_type cell, LyXAlignment align,
1051                               bool onlycolumn)
1052 {
1053         if (!isMultiColumn(cell) || onlycolumn)
1054                 column_info[cellColumn(cell)].alignment = align;
1055         if (!onlycolumn)
1056                 cellinfo_of_cell(cell).alignment = align;
1057 }
1058
1059
1060 void Tabular::setVAlignment(idx_type cell, VAlignment align,
1061                                bool onlycolumn)
1062 {
1063         if (!isMultiColumn(cell) || onlycolumn)
1064                 column_info[cellColumn(cell)].valignment = align;
1065         if (!onlycolumn)
1066                 cellinfo_of_cell(cell).valignment = align;
1067 }
1068
1069
1070 namespace {
1071
1072 /**
1073  * Allow line and paragraph breaks for fixed width cells or disallow them,
1074  * merge cell paragraphs and reset layout to standard for variable width
1075  * cells.
1076  */
1077 void toggleFixedWidth(Cursor & cur, InsetText * inset, bool fixedWidth)
1078 {
1079         inset->setAutoBreakRows(fixedWidth);
1080         if (fixedWidth)
1081                 return;
1082
1083         // merge all paragraphs to one
1084         BufferParams const & bp = cur.bv().buffer().params();
1085         while (inset->paragraphs().size() > 1)
1086                 mergeParagraph(bp, inset->paragraphs(), 0);
1087
1088         // reset layout
1089         cur.push(*inset);
1090         // undo information has already been recorded
1091         inset->getText(0)->setLayout(cur.bv().buffer(), 0, cur.lastpit() + 1,
1092                         bp.getTextClass().defaultLayoutName());
1093         cur.pop();
1094 }
1095
1096 }
1097
1098
1099 void Tabular::setColumnPWidth(Cursor & cur, idx_type cell,
1100                 Length const & width)
1101 {
1102         col_type const j = cellColumn(cell);
1103
1104         column_info[j].p_width = width;
1105         for (row_type i = 0; i < rowCount(); ++i) {
1106                 idx_type const cell = cellIndex(i, j);
1107                 // because of multicolumns
1108                 toggleFixedWidth(cur, getCellInset(cell).get(),
1109                                  !getPWidth(cell).zero());
1110         }
1111         // cur paragraph can become invalid after paragraphs were merged
1112         if (cur.pit() > cur.lastpit())
1113                 cur.pit() = cur.lastpit();
1114         // cur position can become invalid after newlines were removed
1115         if (cur.pos() > cur.lastpos())
1116                 cur.pos() = cur.lastpos();
1117 }
1118
1119
1120 bool Tabular::setMColumnPWidth(Cursor & cur, idx_type cell,
1121                 Length const & width)
1122 {
1123         if (!isMultiColumn(cell))
1124                 return false;
1125
1126         cellinfo_of_cell(cell).p_width = width;
1127         toggleFixedWidth(cur, getCellInset(cell).get(), !width.zero());
1128         // cur paragraph can become invalid after paragraphs were merged
1129         if (cur.pit() > cur.lastpit())
1130                 cur.pit() = cur.lastpit();
1131         // cur position can become invalid after newlines were removed
1132         if (cur.pos() > cur.lastpos())
1133                 cur.pos() = cur.lastpos();
1134         return true;
1135 }
1136
1137
1138 void Tabular::setAlignSpecial(idx_type cell, docstring const & special,
1139                                  Tabular::Feature what)
1140 {
1141         if (what == SET_SPECIAL_MULTI)
1142                 cellinfo_of_cell(cell).align_special = special;
1143         else
1144                 column_info[cellColumn(cell)].align_special = special;
1145 }
1146
1147
1148 void Tabular::setAllLines(idx_type cell, bool line)
1149 {
1150         setTopLine(cell, line);
1151         setBottomLine(cell, line);
1152         setRightLine(cell, line);
1153         setLeftLine(cell, line);
1154 }
1155
1156
1157 void Tabular::setTopLine(idx_type cell, bool line, bool wholerow)
1158 {
1159         row_type const row = cellRow(cell);
1160         if (wholerow || !isMultiColumn(cell))
1161                 row_info[row].top_line = line;
1162         else
1163                 cellinfo_of_cell(cell).top_line = line;
1164 }
1165
1166
1167 void Tabular::setBottomLine(idx_type cell, bool line, bool wholerow)
1168 {
1169         if (wholerow || !isMultiColumn(cell))
1170                 row_info[cellRow(cell)].bottom_line = line;
1171         else
1172                 cellinfo_of_cell(cell).bottom_line = line;
1173 }
1174
1175
1176 void Tabular::setLeftLine(idx_type cell, bool line, bool wholecolumn)
1177 {
1178         if (wholecolumn || !isMultiColumn(cell))
1179                 column_info[cellColumn(cell)].left_line = line;
1180         else
1181                 cellinfo_of_cell(cell).left_line = line;
1182 }
1183
1184
1185 void Tabular::setRightLine(idx_type cell, bool line, bool wholecolumn)
1186 {
1187         if (wholecolumn || !isMultiColumn(cell))
1188                 column_info[cellRightColumn(cell)].right_line = line;
1189         else
1190                 cellinfo_of_cell(cell).right_line = line;
1191 }
1192
1193
1194 LyXAlignment Tabular::getAlignment(idx_type cell, bool onlycolumn) const
1195 {
1196         if (!onlycolumn && isMultiColumn(cell))
1197                 return cellinfo_of_cell(cell).alignment;
1198         return column_info[cellColumn(cell)].alignment;
1199 }
1200
1201
1202 Tabular::VAlignment
1203 Tabular::getVAlignment(idx_type cell, bool onlycolumn) const
1204 {
1205         if (!onlycolumn && isMultiColumn(cell))
1206                 return cellinfo_of_cell(cell).valignment;
1207         return column_info[cellColumn(cell)].valignment;
1208 }
1209
1210
1211 Length const Tabular::getPWidth(idx_type cell) const
1212 {
1213         if (isMultiColumn(cell))
1214                 return cellinfo_of_cell(cell).p_width;
1215         return column_info[cellColumn(cell)].p_width;
1216 }
1217
1218
1219 Length const Tabular::getColumnPWidth(idx_type cell) const
1220 {
1221         return column_info[cellColumn(cell)].p_width;
1222 }
1223
1224
1225 Length const Tabular::getMColumnPWidth(idx_type cell) const
1226 {
1227         if (isMultiColumn(cell))
1228                 return cellinfo_of_cell(cell).p_width;
1229         return Length();
1230 }
1231
1232
1233 docstring const Tabular::getAlignSpecial(idx_type cell, int what) const
1234 {
1235         if (what == SET_SPECIAL_MULTI)
1236                 return cellinfo_of_cell(cell).align_special;
1237         return column_info[cellColumn(cell)].align_special;
1238 }
1239
1240
1241 int Tabular::cellWidth(idx_type cell) const
1242 {
1243         row_type const row = cellRow(cell);
1244         col_type const column1 = cellColumn(cell);
1245         col_type const column2 = cellRightColumn(cell);
1246         int result = 0;
1247         for (col_type i = column1; i <= column2; ++i)
1248                 result += cell_info[row][i].width;
1249         return result;
1250 }
1251
1252
1253 int Tabular::getBeginningOfTextInCell(idx_type cell) const
1254 {
1255         int x = 0;
1256
1257         switch (getAlignment(cell)) {
1258         case LYX_ALIGN_CENTER:
1259                 x += (columnWidth(cell) - cellWidth(cell)) / 2;
1260                 break;
1261         case LYX_ALIGN_RIGHT:
1262                 x += columnWidth(cell) - cellWidth(cell);
1263                 // + getAdditionalWidth(cell);
1264                 break;
1265         default:
1266                 // LYX_ALIGN_LEFT: nothing :-)
1267                 break;
1268         }
1269
1270         // the LaTeX Way :-(
1271         x += WIDTH_OF_LINE;
1272         return x;
1273 }
1274
1275
1276 bool Tabular::isFirstCellInRow(idx_type cell) const
1277 {
1278         return cellColumn(cell) == 0;
1279 }
1280
1281
1282 Tabular::idx_type Tabular::getFirstCellInRow(row_type row) const
1283 {
1284         if (row > rowCount() - 1)
1285                 row = rowCount() - 1;
1286         return cell_info[row][0].cellno;
1287 }
1288
1289
1290 bool Tabular::isLastCellInRow(idx_type cell) const
1291 {
1292         return cellRightColumn(cell) == columnCount() - 1;
1293 }
1294
1295
1296 Tabular::idx_type Tabular::getLastCellInRow(row_type row) const
1297 {
1298         if (row > rowCount() - 1)
1299                 row = rowCount() - 1;
1300         return cell_info[row][columnCount() - 1].cellno;
1301 }
1302
1303
1304 void Tabular::calculate_width_of_column(col_type column)
1305 {
1306         int maximum = 0;
1307         for (row_type i = 0; i < rowCount(); ++i)
1308                 maximum = max(cell_info[i][column].width, maximum);
1309         column_info[column].width = maximum;
1310 }
1311
1312
1313 //
1314 // Calculate the columns regarding ONLY the normal cells and if this
1315 // column is inside a multicolumn cell then use it only if its the last
1316 // column of this multicolumn cell as this gives an added width to the
1317 // column, all the rest should be adapted!
1318 //
1319 bool Tabular::calculate_width_of_column_NMC(col_type column)
1320 {
1321         int const old_column_width = column_info[column].width;
1322         int max = 0;
1323         for (row_type i = 0; i < rowCount(); ++i) {
1324                 idx_type cell = cellIndex(i, column);
1325                 bool ismulti = isMultiColumnReal(cell);
1326                 if ((!ismulti || column == cellRightColumn(cell)) &&
1327                         cell_info[i][column].width > max)
1328                 {
1329                         max = cell_info[i][column].width;
1330                 }
1331         }
1332         column_info[column].width = max;
1333         return column_info[column].width != old_column_width;
1334 }
1335
1336
1337 Tabular::row_type Tabular::cellRow(idx_type cell) const
1338 {
1339         if (cell >= cellCount())
1340                 return rowCount() - 1;
1341         if (cell == npos)
1342                 return 0;
1343         return rowofcell[cell];
1344 }
1345
1346
1347 Tabular::col_type Tabular::cellColumn(idx_type cell) const
1348 {
1349         if (cell >= cellCount())
1350                 return columnCount() - 1;
1351         if (cell == npos)
1352                 return 0;
1353         return columnofcell[cell];
1354 }
1355
1356
1357 Tabular::col_type Tabular::cellRightColumn(idx_type cell) const
1358 {
1359         row_type const row = cellRow(cell);
1360         col_type column = cellColumn(cell);
1361         while (column < columnCount() - 1 &&
1362                    cell_info[row][column + 1].multicolumn == Tabular::CELL_PART_OF_MULTICOLUMN)
1363                 ++column;
1364         return column;
1365 }
1366
1367
1368 void Tabular::write(Buffer const & buf, ostream & os) const
1369 {
1370         // header line
1371         os << "<lyxtabular"
1372            << write_attribute("version", 3)
1373            << write_attribute("rows", rowCount())
1374            << write_attribute("columns", columnCount())
1375            << ">\n";
1376         // global longtable options
1377         os << "<features"
1378            << write_attribute("rotate", rotate)
1379            << write_attribute("booktabs", use_booktabs)
1380            << write_attribute("islongtable", is_long_tabular)
1381            << write_attribute("firstHeadTopDL", endfirsthead.topDL)
1382            << write_attribute("firstHeadBottomDL", endfirsthead.bottomDL)
1383            << write_attribute("firstHeadEmpty", endfirsthead.empty)
1384            << write_attribute("headTopDL", endhead.topDL)
1385            << write_attribute("headBottomDL", endhead.bottomDL)
1386            << write_attribute("footTopDL", endfoot.topDL)
1387            << write_attribute("footBottomDL", endfoot.bottomDL)
1388            << write_attribute("lastFootTopDL", endlastfoot.topDL)
1389            << write_attribute("lastFootBottomDL", endlastfoot.bottomDL)
1390            << write_attribute("lastFootEmpty", endlastfoot.empty)
1391            << ">\n";
1392         for (col_type j = 0; j < columnCount(); ++j) {
1393                 os << "<column"
1394                    << write_attribute("alignment", column_info[j].alignment)
1395                    << write_attribute("valignment", column_info[j].valignment)
1396                    << write_attribute("leftline", column_info[j].left_line)
1397                    << write_attribute("rightline", column_info[j].right_line)
1398                    << write_attribute("width", column_info[j].p_width.asString())
1399                    << write_attribute("special", column_info[j].align_special)
1400                    << ">\n";
1401         }
1402         for (row_type i = 0; i < rowCount(); ++i) {
1403                 os << "<row"
1404                    << write_attribute("topline", row_info[i].top_line)
1405                    << write_attribute("bottomline", row_info[i].bottom_line);
1406                 static const string def("default");
1407                 if (row_info[i].top_space_default)
1408                         os << write_attribute("topspace", def);
1409                 else
1410                         os << write_attribute("topspace", row_info[i].top_space);
1411                 if (row_info[i].bottom_space_default)
1412                         os << write_attribute("bottomspace", def);
1413                 else
1414                         os << write_attribute("bottomspace", row_info[i].bottom_space);
1415                 if (row_info[i].interline_space_default)
1416                         os << write_attribute("interlinespace", def);
1417                 else
1418                         os << write_attribute("interlinespace", row_info[i].interline_space);
1419                 os << write_attribute("endhead", row_info[i].endhead)
1420                    << write_attribute("endfirsthead", row_info[i].endfirsthead)
1421                    << write_attribute("endfoot", row_info[i].endfoot)
1422                    << write_attribute("endlastfoot", row_info[i].endlastfoot)
1423                    << write_attribute("newpage", row_info[i].newpage)
1424                    << ">\n";
1425                 for (col_type j = 0; j < columnCount(); ++j) {
1426                         os << "<cell"
1427                            << write_attribute("multicolumn", cell_info[i][j].multicolumn)
1428                            << write_attribute("alignment", cell_info[i][j].alignment)
1429                            << write_attribute("valignment", cell_info[i][j].valignment)
1430                            << write_attribute("topline", cell_info[i][j].top_line)
1431                            << write_attribute("bottomline", cell_info[i][j].bottom_line)
1432                            << write_attribute("leftline", cell_info[i][j].left_line)
1433                            << write_attribute("rightline", cell_info[i][j].right_line)
1434                            << write_attribute("rotate", cell_info[i][j].rotate)
1435                            << write_attribute("usebox", cell_info[i][j].usebox)
1436                            << write_attribute("width", cell_info[i][j].p_width)
1437                            << write_attribute("special", cell_info[i][j].align_special)
1438                            << ">\n";
1439                         os << "\\begin_inset ";
1440                         cell_info[i][j].inset->write(buf, os);
1441                         os << "\n\\end_inset\n"
1442                            << "</cell>\n";
1443                 }
1444                 os << "</row>\n";
1445         }
1446         os << "</lyxtabular>\n";
1447 }
1448
1449
1450 void Tabular::read(Buffer const & buf, Lexer & lex)
1451 {
1452         string line;
1453         istream & is = lex.getStream();
1454
1455         l_getline(is, line);
1456         if (!prefixIs(line, "<lyxtabular ")
1457                 && !prefixIs(line, "<Tabular ")) {
1458                 BOOST_ASSERT(false);
1459                 return;
1460         }
1461
1462         int version;
1463         if (!getTokenValue(line, "version", version))
1464                 return;
1465         BOOST_ASSERT(version >= 2);
1466
1467         int rows_arg;
1468         if (!getTokenValue(line, "rows", rows_arg))
1469                 return;
1470         int columns_arg;
1471         if (!getTokenValue(line, "columns", columns_arg))
1472                 return;
1473         init(buf.params(), rows_arg, columns_arg);
1474         l_getline(is, line);
1475         if (!prefixIs(line, "<features")) {
1476                 lyxerr << "Wrong tabular format (expected <features ...> got"
1477                        << line << ')' << endl;
1478                 return;
1479         }
1480         getTokenValue(line, "rotate", rotate);
1481         getTokenValue(line, "booktabs", use_booktabs);
1482         getTokenValue(line, "islongtable", is_long_tabular);
1483         getTokenValue(line, "firstHeadTopDL", endfirsthead.topDL);
1484         getTokenValue(line, "firstHeadBottomDL", endfirsthead.bottomDL);
1485         getTokenValue(line, "firstHeadEmpty", endfirsthead.empty);
1486         getTokenValue(line, "headTopDL", endhead.topDL);
1487         getTokenValue(line, "headBottomDL", endhead.bottomDL);
1488         getTokenValue(line, "footTopDL", endfoot.topDL);
1489         getTokenValue(line, "footBottomDL", endfoot.bottomDL);
1490         getTokenValue(line, "lastFootTopDL", endlastfoot.topDL);
1491         getTokenValue(line, "lastFootBottomDL", endlastfoot.bottomDL);
1492         getTokenValue(line, "lastFootEmpty", endlastfoot.empty);
1493
1494         for (col_type j = 0; j < columnCount(); ++j) {
1495                 l_getline(is,line);
1496                 if (!prefixIs(line,"<column")) {
1497                         lyxerr << "Wrong tabular format (expected <column ...> got"
1498                                << line << ')' << endl;
1499                         return;
1500                 }
1501                 getTokenValue(line, "alignment", column_info[j].alignment);
1502                 getTokenValue(line, "valignment", column_info[j].valignment);
1503                 getTokenValue(line, "leftline", column_info[j].left_line);
1504                 getTokenValue(line, "rightline", column_info[j].right_line);
1505                 getTokenValue(line, "width", column_info[j].p_width);
1506                 getTokenValue(line, "special", column_info[j].align_special);
1507         }
1508
1509         for (row_type i = 0; i < rowCount(); ++i) {
1510                 l_getline(is, line);
1511                 if (!prefixIs(line, "<row")) {
1512                         lyxerr << "Wrong tabular format (expected <row ...> got"
1513                                << line << ')' << endl;
1514                         return;
1515                 }
1516                 getTokenValue(line, "topline", row_info[i].top_line);
1517                 getTokenValue(line, "bottomline", row_info[i].bottom_line);
1518                 getTokenValue(line, "topspace", row_info[i].top_space,
1519                               row_info[i].top_space_default);
1520                 getTokenValue(line, "bottomspace", row_info[i].bottom_space,
1521                               row_info[i].bottom_space_default);
1522                 getTokenValue(line, "interlinespace", row_info[i].interline_space,
1523                               row_info[i].interline_space_default);
1524                 getTokenValue(line, "endfirsthead", row_info[i].endfirsthead);
1525                 getTokenValue(line, "endhead", row_info[i].endhead);
1526                 getTokenValue(line, "endfoot", row_info[i].endfoot);
1527                 getTokenValue(line, "endlastfoot", row_info[i].endlastfoot);
1528                 getTokenValue(line, "newpage", row_info[i].newpage);
1529                 for (col_type j = 0; j < columnCount(); ++j) {
1530                         l_getline(is, line);
1531                         if (!prefixIs(line, "<cell")) {
1532                                 lyxerr << "Wrong tabular format (expected <cell ...> got"
1533                                        << line << ')' << endl;
1534                                 return;
1535                         }
1536                         getTokenValue(line, "multicolumn", cell_info[i][j].multicolumn);
1537                         getTokenValue(line, "alignment", cell_info[i][j].alignment);
1538                         getTokenValue(line, "valignment", cell_info[i][j].valignment);
1539                         getTokenValue(line, "topline", cell_info[i][j].top_line);
1540                         getTokenValue(line, "bottomline", cell_info[i][j].bottom_line);
1541                         getTokenValue(line, "leftline", cell_info[i][j].left_line);
1542                         getTokenValue(line, "rightline", cell_info[i][j].right_line);
1543                         getTokenValue(line, "rotate", cell_info[i][j].rotate);
1544                         getTokenValue(line, "usebox", cell_info[i][j].usebox);
1545                         getTokenValue(line, "width", cell_info[i][j].p_width);
1546                         getTokenValue(line, "special", cell_info[i][j].align_special);
1547                         l_getline(is, line);
1548                         if (prefixIs(line, "\\begin_inset")) {
1549                                 cell_info[i][j].inset->read(buf, lex);
1550                                 l_getline(is, line);
1551                         }
1552                         if (!prefixIs(line, "</cell>")) {
1553                                 lyxerr << "Wrong tabular format (expected </cell> got"
1554                                        << line << ')' << endl;
1555                                 return;
1556                         }
1557                 }
1558                 l_getline(is, line);
1559                 if (!prefixIs(line, "</row>")) {
1560                         lyxerr << "Wrong tabular format (expected </row> got"
1561                                << line << ')' << endl;
1562                         return;
1563                 }
1564         }
1565         while (!prefixIs(line, "</lyxtabular>")) {
1566                 l_getline(is, line);
1567         }
1568         updateIndexes();
1569 }
1570
1571
1572 bool Tabular::isMultiColumn(idx_type cell) const
1573 {
1574         return cellinfo_of_cell(cell).multicolumn != Tabular::CELL_NORMAL;
1575 }
1576
1577
1578 bool Tabular::isMultiColumnReal(idx_type cell) const
1579 {
1580         return cellColumn(cell) != cellRightColumn(cell) &&
1581                         cellinfo_of_cell(cell).multicolumn != Tabular::CELL_NORMAL;
1582 }
1583
1584
1585 Tabular::cellstruct & Tabular::cellinfo_of_cell(idx_type cell) const
1586 {
1587         return cell_info[cellRow(cell)][cellColumn(cell)];
1588 }
1589
1590
1591 void Tabular::setMultiColumn(Buffer * buffer, idx_type cell,
1592                                 idx_type number)
1593 {
1594         cellstruct & cs = cellinfo_of_cell(cell);
1595         cs.multicolumn = CELL_BEGIN_OF_MULTICOLUMN;
1596         cs.alignment = column_info[cellColumn(cell)].alignment;
1597         cs.top_line = row_info[cellRow(cell)].top_line;
1598         cs.bottom_line = row_info[cellRow(cell)].bottom_line;
1599         cs.left_line = column_info[cellColumn(cell)].left_line;
1600         cs.right_line = column_info[cellColumn(cell+number-1)].right_line;
1601         for (idx_type i = 1; i < number; ++i) {
1602                 cellstruct & cs1 = cellinfo_of_cell(cell + i);
1603                 cs1.multicolumn = CELL_PART_OF_MULTICOLUMN;
1604                 cs.inset->appendParagraphs(buffer, cs1.inset->paragraphs());
1605                 cs1.inset->clear();
1606         }
1607         updateIndexes();
1608 }
1609
1610
1611 Tabular::idx_type Tabular::cells_in_multicolumn(idx_type cell) const
1612 {
1613         row_type const row = cellRow(cell);
1614         col_type column = cellColumn(cell);
1615         idx_type result = 1;
1616         ++column;
1617         while (column < columnCount() &&
1618                    cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN)
1619         {
1620                 ++result;
1621                 ++column;
1622         }
1623         return result;
1624 }
1625
1626
1627 Tabular::idx_type Tabular::unsetMultiColumn(idx_type cell)
1628 {
1629         row_type const row = cellRow(cell);
1630         col_type column = cellColumn(cell);
1631
1632         idx_type result = 0;
1633
1634         if (cell_info[row][column].multicolumn == CELL_BEGIN_OF_MULTICOLUMN) {
1635                 cell_info[row][column].multicolumn = CELL_NORMAL;
1636                 ++column;
1637                 while (column < columnCount() &&
1638                            cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN)
1639                 {
1640                         cell_info[row][column].multicolumn = CELL_NORMAL;
1641                         ++column;
1642                         ++result;
1643                 }
1644         }
1645         updateIndexes();
1646         return result;
1647 }
1648
1649
1650 void Tabular::setBookTabs(bool what)
1651 {
1652         use_booktabs = what;
1653 }
1654
1655
1656 bool Tabular::useBookTabs() const
1657 {
1658         return use_booktabs;
1659 }
1660
1661
1662 void Tabular::setLongTabular(bool what)
1663 {
1664         is_long_tabular = what;
1665 }
1666
1667
1668 bool Tabular::isLongTabular() const
1669 {
1670         return is_long_tabular;
1671 }
1672
1673
1674 void Tabular::setRotateTabular(bool flag)
1675 {
1676         rotate = flag;
1677 }
1678
1679
1680 bool Tabular::getRotateTabular() const
1681 {
1682         return rotate;
1683 }
1684
1685
1686 void Tabular::setRotateCell(idx_type cell, bool flag)
1687 {
1688         cellinfo_of_cell(cell).rotate = flag;
1689 }
1690
1691
1692 bool Tabular::getRotateCell(idx_type cell) const
1693 {
1694         return cellinfo_of_cell(cell).rotate;
1695 }
1696
1697
1698 bool Tabular::needRotating() const
1699 {
1700         if (rotate)
1701                 return true;
1702         for (row_type i = 0; i < rowCount(); ++i)
1703                 for (col_type j = 0; j < columnCount(); ++j)
1704                         if (cell_info[i][j].rotate)
1705                                 return true;
1706         return false;
1707 }
1708
1709
1710 bool Tabular::isLastCell(idx_type cell) const
1711 {
1712         if (cell + 1 < cellCount())
1713                 return false;
1714         return true;
1715 }
1716
1717
1718 Tabular::idx_type Tabular::getCellAbove(idx_type cell) const
1719 {
1720         if (cellRow(cell) > 0)
1721                 return cell_info[cellRow(cell)-1][cellColumn(cell)].cellno;
1722         return cell;
1723 }
1724
1725
1726 Tabular::idx_type Tabular::getCellBelow(idx_type cell) const
1727 {
1728         if (cellRow(cell) + 1 < rowCount())
1729                 return cell_info[cellRow(cell)+1][cellColumn(cell)].cellno;
1730         return cell;
1731 }
1732
1733
1734 Tabular::idx_type Tabular::getLastCellAbove(idx_type cell) const
1735 {
1736         if (cellRow(cell) == 0)
1737                 return cell;
1738         if (!isMultiColumn(cell))
1739                 return getCellAbove(cell);
1740         return cell_info[cellRow(cell) - 1][cellRightColumn(cell)].cellno;
1741 }
1742
1743
1744 Tabular::idx_type Tabular::getLastCellBelow(idx_type cell) const
1745 {
1746         if (cellRow(cell) + 1 >= rowCount())
1747                 return cell;
1748         if (!isMultiColumn(cell))
1749                 return getCellBelow(cell);
1750         return cell_info[cellRow(cell) + 1][cellRightColumn(cell)].cellno;
1751 }
1752
1753
1754 Tabular::idx_type Tabular::cellIndex(row_type row,
1755                                                col_type column) const
1756 {
1757         BOOST_ASSERT(column != npos && column < columnCount() &&
1758                      row    != npos && row    < rowCount());
1759         return cell_info[row][column].cellno;
1760 }
1761
1762
1763 void Tabular::setUsebox(idx_type cell, BoxType type)
1764 {
1765         cellinfo_of_cell(cell).usebox = type;
1766 }
1767
1768
1769 Tabular::BoxType Tabular::getUsebox(idx_type cell) const
1770 {
1771         if (column_info[cellColumn(cell)].p_width.zero() &&
1772                 !(isMultiColumn(cell) && !cellinfo_of_cell(cell).p_width.zero()))
1773                 return BOX_NONE;
1774         if (cellinfo_of_cell(cell).usebox > 1)
1775                 return cellinfo_of_cell(cell).usebox;
1776         return useParbox(cell);
1777 }
1778
1779
1780 ///
1781 //  This are functions used for the longtable support
1782 ///
1783 void Tabular::setLTHead(row_type row, bool flag, ltType const & hd,
1784                            bool first)
1785 {
1786         if (first) {
1787                 endfirsthead = hd;
1788                 if (hd.set)
1789                         row_info[row].endfirsthead = flag;
1790         } else {
1791                 endhead = hd;
1792                 if (hd.set)
1793                         row_info[row].endhead = flag;
1794         }
1795 }
1796
1797
1798 bool Tabular::getRowOfLTHead(row_type row, ltType & hd) const
1799 {
1800         hd = endhead;
1801         hd.set = haveLTHead();
1802         return row_info[row].endhead;
1803 }
1804
1805
1806 bool Tabular::getRowOfLTFirstHead(row_type row, ltType & hd) const
1807 {
1808         hd = endfirsthead;
1809         hd.set = haveLTFirstHead();
1810         return row_info[row].endfirsthead;
1811 }
1812
1813
1814 void Tabular::setLTFoot(row_type row, bool flag, ltType const & fd,
1815                            bool last)
1816 {
1817         if (last) {
1818                 endlastfoot = fd;
1819                 if (fd.set)
1820                         row_info[row].endlastfoot = flag;
1821         } else {
1822                 endfoot = fd;
1823                 if (fd.set)
1824                         row_info[row].endfoot = flag;
1825         }
1826 }
1827
1828
1829 bool Tabular::getRowOfLTFoot(row_type row, ltType & fd) const
1830 {
1831         fd = endfoot;
1832         fd.set = haveLTFoot();
1833         return row_info[row].endfoot;
1834 }
1835
1836
1837 bool Tabular::getRowOfLTLastFoot(row_type row, ltType & fd) const
1838 {
1839         fd = endlastfoot;
1840         fd.set = haveLTLastFoot();
1841         return row_info[row].endlastfoot;
1842 }
1843
1844
1845 void Tabular::setLTNewPage(row_type row, bool what)
1846 {
1847         row_info[row].newpage = what;
1848 }
1849
1850
1851 bool Tabular::getLTNewPage(row_type row) const
1852 {
1853         return row_info[row].newpage;
1854 }
1855
1856
1857 bool Tabular::haveLTHead() const
1858 {
1859         for (row_type i = 0; i < rowCount(); ++i)
1860                 if (row_info[i].endhead)
1861                         return true;
1862         return false;
1863 }
1864
1865
1866 bool Tabular::haveLTFirstHead() const
1867 {
1868         if (endfirsthead.empty)
1869                 return false;
1870         for (row_type i = 0; i < rowCount(); ++i)
1871                 if (row_info[i].endfirsthead)
1872                         return true;
1873         return false;
1874 }
1875
1876
1877 bool Tabular::haveLTFoot() const
1878 {
1879         for (row_type i = 0; i < rowCount(); ++i)
1880                 if (row_info[i].endfoot)
1881                         return true;
1882         return false;
1883 }
1884
1885
1886 bool Tabular::haveLTLastFoot() const
1887 {
1888         if (endlastfoot.empty)
1889                 return false;
1890         for (row_type i = 0; i < rowCount(); ++i)
1891                 if (row_info[i].endlastfoot)
1892                         return true;
1893         return false;
1894 }
1895
1896
1897 // end longtable support functions
1898
1899 void Tabular::setRowAscent(row_type row, int height)
1900 {
1901         if (row >= rowCount() || row_info[row].ascent == height)
1902                 return;
1903         row_info[row].ascent = height;
1904 }
1905
1906
1907 void Tabular::setRowDescent(row_type row, int height)
1908 {
1909         if (row >= rowCount() || row_info[row].descent == height)
1910                 return;
1911         row_info[row].descent = height;
1912 }
1913
1914
1915 int Tabular::rowAscent(row_type row) const
1916 {
1917         if (row >= rowCount())
1918                 return 0;
1919         return row_info[row].ascent;
1920 }
1921
1922
1923 int Tabular::rowDescent(row_type row) const
1924 {
1925         BOOST_ASSERT(row < rowCount());
1926         return row_info[row].descent;
1927 }
1928
1929
1930 int Tabular::height() const
1931 {
1932         int height = 0;
1933         for (row_type row = 0; row < rowCount(); ++row)
1934                 height += rowAscent(row) + rowDescent(row) +
1935                         getAdditionalHeight(row);
1936         return height;
1937 }
1938
1939
1940 bool Tabular::isPartOfMultiColumn(row_type row, col_type column) const
1941 {
1942         BOOST_ASSERT(row < rowCount());
1943         BOOST_ASSERT(column < columnCount());
1944         return cell_info[row][column].multicolumn == CELL_PART_OF_MULTICOLUMN;
1945 }
1946
1947
1948 int Tabular::TeXTopHLine(odocstream & os, row_type row) const
1949 {
1950         // FIXME: assert or return 0 as in TeXBottomHLine()?
1951         BOOST_ASSERT(row != npos);
1952         BOOST_ASSERT(row < rowCount());
1953
1954         idx_type const fcell = getFirstCellInRow(row);
1955         idx_type const n = numberOfCellsInRow(fcell) + fcell;
1956         idx_type tmp = 0;
1957
1958         for (idx_type i = fcell; i < n; ++i) {
1959                 if (topLine(i))
1960                         ++tmp;
1961         }
1962         if (use_booktabs && row == 0) {
1963                 os << "\\toprule ";
1964         } else if (tmp == n - fcell) {
1965                 os << (use_booktabs ? "\\midrule " : "\\hline ");
1966         } else if (tmp) {
1967                 for (idx_type i = fcell; i < n; ++i) {
1968                         if (topLine(i)) {
1969                                 os << (use_booktabs ? "\\cmidrule{" : "\\cline{")
1970                                    << cellColumn(i) + 1
1971                                    << '-'
1972                                    << cellRightColumn(i) + 1
1973                                    << "} ";
1974                         }
1975                 }
1976         } else {
1977                 return 0;
1978         }
1979         os << "\n";
1980         return 1;
1981 }
1982
1983
1984 int Tabular::TeXBottomHLine(odocstream & os, row_type row) const
1985 {
1986         // FIXME: return 0 or assert as in TeXTopHLine()?
1987         if (row == npos || row >= rowCount())
1988                 return 0;
1989
1990         idx_type const fcell = getFirstCellInRow(row);
1991         idx_type const n = numberOfCellsInRow(fcell) + fcell;
1992         idx_type tmp = 0;
1993
1994         for (idx_type i = fcell; i < n; ++i) {
1995                 if (bottomLine(i))
1996                         ++tmp;
1997         }
1998         if (use_booktabs && row == rowCount() - 1) {
1999                 os << "\\bottomrule";
2000         } else if (tmp == n - fcell) {
2001                 os << (use_booktabs ? "\\midrule" : "\\hline");
2002         } else if (tmp) {
2003                 for (idx_type i = fcell; i < n; ++i) {
2004                         if (bottomLine(i)) {
2005                                 os << (use_booktabs ? "\\cmidrule{" : "\\cline{")
2006                                    << cellColumn(i) + 1
2007                                    << '-'
2008                                    << cellRightColumn(i) + 1
2009                                    << "} ";
2010                         }
2011                 }
2012         } else {
2013                 return 0;
2014         }
2015         os << "\n";
2016         return 1;
2017 }
2018
2019
2020 int Tabular::TeXCellPreamble(odocstream & os, idx_type cell) const
2021 {
2022         int ret = 0;
2023
2024         if (getRotateCell(cell)) {
2025                 os << "\\begin{sideways}\n";
2026                 ++ret;
2027         }
2028         if (isMultiColumn(cell)) {
2029                 os << "\\multicolumn{" << cells_in_multicolumn(cell) << "}{";
2030                 if (leftLine(cell) &&
2031                         (isFirstCellInRow(cell) ||
2032                          (!isMultiColumn(cell - 1) && !leftLine(cell, true) &&
2033                           !rightLine(cell - 1, true))))
2034                         os << '|';
2035                 if (!cellinfo_of_cell(cell).align_special.empty()) {
2036                         os << cellinfo_of_cell(cell).align_special;
2037                 } else {
2038                         if (!getPWidth(cell).zero()) {
2039                                 switch (getVAlignment(cell)) {
2040                                 case LYX_VALIGN_TOP:
2041                                         os << 'p';
2042                                         break;
2043                                 case LYX_VALIGN_MIDDLE:
2044                                         os << 'm';
2045                                         break;
2046                                 case LYX_VALIGN_BOTTOM:
2047                                         os << 'b';
2048                                         break;
2049                                 }
2050                                 os << '{'
2051                                    << from_ascii(getPWidth(cell).asLatexString())
2052                                    << '}';
2053                         } else {
2054                                 switch (getAlignment(cell)) {
2055                                 case LYX_ALIGN_LEFT:
2056                                         os << 'l';
2057                                         break;
2058                                 case LYX_ALIGN_RIGHT:
2059                                         os << 'r';
2060                                         break;
2061                                 default:
2062                                         os << 'c';
2063                                         break;
2064                                 }
2065                         } // end if else !getPWidth
2066                 } // end if else !cellinfo_of_cell
2067                 if (rightLine(cell))
2068                         os << '|';
2069                 if (((cell + 1) < cellCount()) && !isFirstCellInRow(cell+1) &&
2070                         leftLine(cell+1))
2071                         os << '|';
2072                 os << "}{";
2073                 }
2074         if (getUsebox(cell) == BOX_PARBOX) {
2075                 os << "\\parbox[";
2076                 switch (getVAlignment(cell)) {
2077                 case LYX_VALIGN_TOP:
2078                         os << 't';
2079                         break;
2080                 case LYX_VALIGN_MIDDLE:
2081                         os << 'c';
2082                         break;
2083                 case LYX_VALIGN_BOTTOM:
2084                         os << 'b';
2085                         break;
2086                 }
2087                 os << "]{" << from_ascii(getPWidth(cell).asLatexString())
2088                    << "}{";
2089         } else if (getUsebox(cell) == BOX_MINIPAGE) {
2090                 os << "\\begin{minipage}[";
2091                 switch (getVAlignment(cell)) {
2092                 case LYX_VALIGN_TOP:
2093                         os << 't';
2094                         break;
2095                 case LYX_VALIGN_MIDDLE:
2096                         os << 'm';
2097                         break;
2098                 case LYX_VALIGN_BOTTOM:
2099                         os << 'b';
2100                         break;
2101                 }
2102                 os << "]{" << from_ascii(getPWidth(cell).asLatexString())
2103                    << "}\n";
2104                 ++ret;
2105         }
2106         return ret;
2107 }
2108
2109
2110 int Tabular::TeXCellPostamble(odocstream & os, idx_type cell) const
2111 {
2112         int ret = 0;
2113
2114         // usual cells
2115         if (getUsebox(cell) == BOX_PARBOX)
2116                 os << '}';
2117         else if (getUsebox(cell) == BOX_MINIPAGE) {
2118                 os << "%\n\\end{minipage}";
2119                 ret += 2;
2120         }
2121         if (isMultiColumn(cell)) {
2122                 os << '}';
2123         }
2124         if (getRotateCell(cell)) {
2125                 os << "%\n\\end{sideways}";
2126                 ++ret;
2127         }
2128         return ret;
2129 }
2130
2131
2132 int Tabular::TeXLongtableHeaderFooter(odocstream & os, Buffer const & buf,
2133                                          OutputParams const & runparams) const
2134 {
2135         if (!is_long_tabular)
2136                 return 0;
2137
2138         int ret = 0;
2139         // output header info
2140         if (haveLTHead()) {
2141                 if (endhead.topDL) {
2142                         os << "\\hline\n";
2143                         ++ret;
2144                 }
2145                 for (row_type i = 0; i < rowCount(); ++i) {
2146                         if (row_info[i].endhead) {
2147                                 ret += TeXRow(os, i, buf, runparams);
2148                         }
2149                 }
2150                 if (endhead.bottomDL) {
2151                         os << "\\hline\n";
2152                         ++ret;
2153                 }
2154                 os << "\\endhead\n";
2155                 ++ret;
2156                 if (endfirsthead.empty) {
2157                         os << "\\endfirsthead\n";
2158                         ++ret;
2159                 }
2160         }
2161         // output firstheader info
2162         if (haveLTFirstHead()) {
2163                 if (endfirsthead.topDL) {
2164                         os << "\\hline\n";
2165                         ++ret;
2166                 }
2167                 for (row_type i = 0; i < rowCount(); ++i) {
2168                         if (row_info[i].endfirsthead) {
2169                                 ret += TeXRow(os, i, buf, runparams);
2170                         }
2171                 }
2172                 if (endfirsthead.bottomDL) {
2173                         os << "\\hline\n";
2174                         ++ret;
2175                 }
2176                 os << "\\endfirsthead\n";
2177                 ++ret;
2178         }
2179         // output footer info
2180         if (haveLTFoot()) {
2181                 if (endfoot.topDL) {
2182                         os << "\\hline\n";
2183                         ++ret;
2184                 }
2185                 for (row_type i = 0; i < rowCount(); ++i) {
2186                         if (row_info[i].endfoot) {
2187                                 ret += TeXRow(os, i, buf, runparams);
2188                         }
2189                 }
2190                 if (endfoot.bottomDL) {
2191                         os << "\\hline\n";
2192                         ++ret;
2193                 }
2194                 os << "\\endfoot\n";
2195                 ++ret;
2196                 if (endlastfoot.empty) {
2197                         os << "\\endlastfoot\n";
2198                         ++ret;
2199                 }
2200         }
2201         // output lastfooter info
2202         if (haveLTLastFoot()) {
2203                 if (endlastfoot.topDL) {
2204                         os << "\\hline\n";
2205                         ++ret;
2206                 }
2207                 for (row_type i = 0; i < rowCount(); ++i) {
2208                         if (row_info[i].endlastfoot) {
2209                                 ret += TeXRow(os, i, buf, runparams);
2210                         }
2211                 }
2212                 if (endlastfoot.bottomDL) {
2213                         os << "\\hline\n";
2214                         ++ret;
2215                 }
2216                 os << "\\endlastfoot\n";
2217                 ++ret;
2218         }
2219         return ret;
2220 }
2221
2222
2223 bool Tabular::isValidRow(row_type row) const
2224 {
2225         if (!is_long_tabular)
2226                 return true;
2227         return !row_info[row].endhead && !row_info[row].endfirsthead &&
2228                         !row_info[row].endfoot && !row_info[row].endlastfoot;
2229 }
2230
2231
2232 int Tabular::TeXRow(odocstream & os, row_type i, Buffer const & buf,
2233                        OutputParams const & runparams) const
2234 {
2235         idx_type cell = cellIndex(i, 0);
2236         int ret = TeXTopHLine(os, i);
2237         if (row_info[i].top_space_default) {
2238                 if (use_booktabs)
2239                         os << "\\addlinespace\n";
2240                 else
2241                         os << "\\noalign{\\vskip\\doublerulesep}\n";
2242                 ++ret;
2243         } else if(!row_info[i].top_space.zero()) {
2244                 if (use_booktabs)
2245                         os << "\\addlinespace["
2246                            << from_ascii(row_info[i].top_space.asLatexString())
2247                            << "]\n";
2248                 else {
2249                         os << "\\noalign{\\vskip"
2250                            << from_ascii(row_info[i].top_space.asLatexString())
2251                            << "}\n";
2252                 }
2253                 ++ret;
2254         }
2255
2256         for (col_type j = 0; j < columnCount(); ++j) {
2257                 if (isPartOfMultiColumn(i, j))
2258                         continue;
2259                 ret += TeXCellPreamble(os, cell);
2260                 shared_ptr<InsetText> inset = getCellInset(cell);
2261
2262                 Paragraph const & par = inset->paragraphs().front();
2263                 bool rtl = par.isRTL(buf.params())
2264                         && !par.empty()
2265                         && getPWidth(cell).zero();
2266
2267                 if (rtl) {
2268                         if (par.getParLanguage(buf.params())->lang() ==
2269                         "farsi")
2270                                 os << "\\textFR{";
2271                         else if (par.getParLanguage(buf.params())->lang() == "arabic_arabi")
2272                                 os << "\\textAR{";
2273                         // currently, remaning RTL languages are arabic_arabtex and hebrew
2274                         else
2275                                 os << "\\R{";
2276                 }
2277                 ret += inset->latex(buf, os, runparams);
2278                 if (rtl)
2279                         os << '}';
2280
2281                 ret += TeXCellPostamble(os, cell);
2282                 if (!isLastCellInRow(cell)) { // not last cell in row
2283                         os << " & ";
2284                 }
2285                 ++cell;
2286         }
2287         os << "\\tabularnewline";
2288         if (row_info[i].bottom_space_default) {
2289                 if (use_booktabs)
2290                         os << "\\addlinespace";
2291                 else
2292                         os << "[\\doublerulesep]";
2293         } else if (!row_info[i].bottom_space.zero()) {
2294                 if (use_booktabs)
2295                         os << "\\addlinespace";
2296                 os << '['
2297                    << from_ascii(row_info[i].bottom_space.asLatexString())
2298                    << ']';
2299         }
2300         os << '\n';
2301         ++ret;
2302         ret += TeXBottomHLine(os, i);
2303         if (row_info[i].interline_space_default) {
2304                 if (use_booktabs)
2305                         os << "\\addlinespace\n";
2306                 else
2307                         os << "\\noalign{\\vskip\\doublerulesep}\n";
2308                 ++ret;
2309         } else if (!row_info[i].interline_space.zero()) {
2310                 if (use_booktabs)
2311                         os << "\\addlinespace["
2312                            << from_ascii(row_info[i].interline_space.asLatexString())
2313                            << "]\n";
2314                 else
2315                         os << "\\noalign{\\vskip"
2316                            << from_ascii(row_info[i].interline_space.asLatexString())
2317                            << "}\n";
2318                 ++ret;
2319         }
2320         return ret;
2321 }
2322
2323
2324 int Tabular::latex(Buffer const & buf, odocstream & os,
2325                       OutputParams const & runparams) const
2326 {
2327         int ret = 0;
2328
2329         //+---------------------------------------------------------------------
2330         //+                      first the opening preamble                    +
2331         //+---------------------------------------------------------------------
2332
2333         if (rotate) {
2334                 os << "\\begin{sideways}\n";
2335                 ++ret;
2336         }
2337         if (is_long_tabular)
2338                 os << "\\begin{longtable}{";
2339         else
2340                 os << "\\begin{tabular}{";
2341         for (col_type i = 0; i < columnCount(); ++i) {
2342                 if (!use_booktabs && column_info[i].left_line)
2343                         os << '|';
2344                 if (!column_info[i].align_special.empty()) {
2345                         os << column_info[i].align_special;
2346                 } else {
2347                         if (!column_info[i].p_width.zero()) {
2348                                 switch (column_info[i].alignment) {
2349                                 case LYX_ALIGN_LEFT:
2350                                         os << ">{\\raggedright}";
2351                                         break;
2352                                 case LYX_ALIGN_RIGHT:
2353                                         os << ">{\\raggedleft}";
2354                                         break;
2355                                 case LYX_ALIGN_CENTER:
2356                                         os << ">{\\centering}";
2357                                         break;
2358                                 case LYX_ALIGN_NONE:
2359                                 case LYX_ALIGN_BLOCK:
2360                                 case LYX_ALIGN_LAYOUT:
2361                                 case LYX_ALIGN_SPECIAL:
2362                                         break;
2363                                 }
2364
2365                                 switch (column_info[i].valignment) {
2366                                 case LYX_VALIGN_TOP:
2367                                         os << 'p';
2368                                         break;
2369                                 case LYX_VALIGN_MIDDLE:
2370                                         os << 'm';
2371                                         break;
2372                                 case LYX_VALIGN_BOTTOM:
2373                                         os << 'b';
2374                                         break;
2375                         }
2376                                 os << '{'
2377                                    << from_ascii(column_info[i].p_width.asLatexString())
2378                                    << '}';
2379                         } else {
2380                                 switch (column_info[i].alignment) {
2381                                 case LYX_ALIGN_LEFT:
2382                                         os << 'l';
2383                                         break;
2384                                 case LYX_ALIGN_RIGHT:
2385                                         os << 'r';
2386                                         break;
2387                                 default:
2388                                         os << 'c';
2389                                         break;
2390                                 }
2391                         } // end if else !column_info[i].p_width
2392                 } // end if else !column_info[i].align_special
2393                 if (!use_booktabs && column_info[i].right_line)
2394                         os << '|';
2395         }
2396         os << "}\n";
2397         ++ret;
2398
2399         ret += TeXLongtableHeaderFooter(os, buf, runparams);
2400
2401         //+---------------------------------------------------------------------
2402         //+                      the single row and columns (cells)            +
2403         //+---------------------------------------------------------------------
2404
2405         for (row_type i = 0; i < rowCount(); ++i) {
2406                 if (isValidRow(i)) {
2407                         ret += TeXRow(os, i, buf, runparams);
2408                         if (is_long_tabular && row_info[i].newpage) {
2409                                 os << "\\newpage\n";
2410                                 ++ret;
2411                         }
2412                 }
2413         }
2414
2415         //+---------------------------------------------------------------------
2416         //+                      the closing of the tabular                    +
2417         //+---------------------------------------------------------------------
2418
2419         if (is_long_tabular)
2420                 os << "\\end{longtable}";
2421         else
2422                 os << "\\end{tabular}";
2423         if (rotate) {
2424                 os << "\n\\end{sideways}";
2425                 ++ret;
2426         }
2427
2428         return ret;
2429 }
2430
2431
2432 int Tabular::docbookRow(Buffer const & buf, odocstream & os, row_type row,
2433                            OutputParams const & runparams) const
2434 {
2435         int ret = 0;
2436         idx_type cell = getFirstCellInRow(row);
2437
2438         os << "<row>\n";
2439         for (col_type j = 0; j < columnCount(); ++j) {
2440                 if (isPartOfMultiColumn(row, j))
2441                         continue;
2442
2443                 os << "<entry align=\"";
2444                 switch (getAlignment(cell)) {
2445                 case LYX_ALIGN_LEFT:
2446                         os << "left";
2447                         break;
2448                 case LYX_ALIGN_RIGHT:
2449                         os << "right";
2450                         break;
2451                 default:
2452                         os << "center";
2453                         break;
2454                 }
2455
2456                 os << "\" valign=\"";
2457                 switch (getVAlignment(cell)) {
2458                 case LYX_VALIGN_TOP:
2459                         os << "top";
2460                         break;
2461                 case LYX_VALIGN_BOTTOM:
2462                         os << "bottom";
2463                         break;
2464                 case LYX_VALIGN_MIDDLE:
2465                         os << "middle";
2466                 }
2467                 os << '"';
2468
2469                 if (isMultiColumn(cell)) {
2470                         os << " namest=\"col" << j << "\" ";
2471                         os << "nameend=\"col" << j + cells_in_multicolumn(cell) - 1<< '"';
2472                 }
2473
2474                 os << '>';
2475                 ret += getCellInset(cell)->docbook(buf, os, runparams);
2476                 os << "</entry>\n";
2477                 ++cell;
2478         }
2479         os << "</row>\n";
2480         return ret;
2481 }
2482
2483
2484 int Tabular::docbook(Buffer const & buf, odocstream & os,
2485                         OutputParams const & runparams) const
2486 {
2487         int ret = 0;
2488
2489         //+---------------------------------------------------------------------
2490         //+                      first the opening preamble                    +
2491         //+---------------------------------------------------------------------
2492
2493         os << "<tgroup cols=\"" << columnCount()
2494            << "\" colsep=\"1\" rowsep=\"1\">\n";
2495
2496         for (col_type i = 0; i < columnCount(); ++i) {
2497                 os << "<colspec colname=\"col" << i << "\" align=\"";
2498                 switch (column_info[i].alignment) {
2499                 case LYX_ALIGN_LEFT:
2500                         os << "left";
2501                         break;
2502                 case LYX_ALIGN_RIGHT:
2503                         os << "right";
2504                         break;
2505                 default:
2506                         os << "center";
2507                         break;
2508                 }
2509                 os << '"';
2510                 if (runparams.flavor == OutputParams::XML)
2511                         os << '/';
2512                 os << ">\n";
2513                 ++ret;
2514         }
2515
2516         //+---------------------------------------------------------------------
2517         //+                      Long Tabular case                             +
2518         //+---------------------------------------------------------------------
2519
2520         // output header info
2521         if (haveLTHead() || haveLTFirstHead()) {
2522                 os << "<thead>\n";
2523                 ++ret;
2524                 for (row_type i = 0; i < rowCount(); ++i) {
2525                         if (row_info[i].endhead || row_info[i].endfirsthead) {
2526                                 ret += docbookRow(buf, os, i, runparams);
2527                         }
2528                 }
2529                 os << "</thead>\n";
2530                 ++ret;
2531         }
2532         // output footer info
2533         if (haveLTFoot() || haveLTLastFoot()) {
2534                 os << "<tfoot>\n";
2535                 ++ret;
2536                 for (row_type i = 0; i < rowCount(); ++i) {
2537                         if (row_info[i].endfoot || row_info[i].endlastfoot) {
2538                                 ret += docbookRow(buf, os, i, runparams);
2539                         }
2540                 }
2541                 os << "</tfoot>\n";
2542                 ++ret;
2543         }
2544
2545         //+---------------------------------------------------------------------
2546         //+                      the single row and columns (cells)            +
2547         //+---------------------------------------------------------------------
2548
2549         os << "<tbody>\n";
2550         ++ret;
2551         for (row_type i = 0; i < rowCount(); ++i) {
2552                 if (isValidRow(i)) {
2553                         ret += docbookRow(buf, os, i, runparams);
2554                 }
2555         }
2556         os << "</tbody>\n";
2557         ++ret;
2558         //+---------------------------------------------------------------------
2559         //+                      the closing of the tabular                    +
2560         //+---------------------------------------------------------------------
2561
2562         os << "</tgroup>";
2563         ++ret;
2564
2565         return ret;
2566 }
2567
2568
2569 bool Tabular::plaintextTopHLine(odocstream & os, row_type row,
2570                                    vector<unsigned int> const & clen) const
2571 {
2572         idx_type const fcell = getFirstCellInRow(row);
2573         idx_type const n = numberOfCellsInRow(fcell) + fcell;
2574         idx_type tmp = 0;
2575
2576         for (idx_type i = fcell; i < n; ++i) {
2577                 if (topLine(i)) {
2578                         ++tmp;
2579                         break;
2580                 }
2581         }
2582         if (!tmp)
2583                 return false;
2584
2585         char_type ch;
2586         for (idx_type i = fcell; i < n; ++i) {
2587                 if (topLine(i)) {
2588                         if (leftLine(i))
2589                                 os << "+-";
2590                         else
2591                                 os << "--";
2592                         ch = '-';
2593                 } else {
2594                         os << "  ";
2595                         ch = ' ';
2596                 }
2597                 col_type column = cellColumn(i);
2598                 int len = clen[column];
2599                 while (column < columnCount() - 1
2600                        && isPartOfMultiColumn(row, ++column))
2601                         len += clen[column] + 4;
2602                 os << docstring(len, ch);
2603                 if (topLine(i)) {
2604                         if (rightLine(i))
2605                                 os << "-+";
2606                         else
2607                                 os << "--";
2608                 } else {
2609                         os << "  ";
2610                 }
2611         }
2612         os << endl;
2613         return true;
2614 }
2615
2616
2617 bool Tabular::plaintextBottomHLine(odocstream & os, row_type row,
2618                                       vector<unsigned int> const & clen) const
2619 {
2620         idx_type const fcell = getFirstCellInRow(row);
2621         idx_type const n = numberOfCellsInRow(fcell) + fcell;
2622         idx_type tmp = 0;
2623
2624         for (idx_type i = fcell; i < n; ++i) {
2625                 if (bottomLine(i)) {
2626                         ++tmp;
2627                         break;
2628                 }
2629         }
2630         if (!tmp)
2631                 return false;
2632
2633         char_type ch;
2634         for (idx_type i = fcell; i < n; ++i) {
2635                 if (bottomLine(i)) {
2636                         if (leftLine(i))
2637                                 os << "+-";
2638                         else
2639                                 os << "--";
2640                         ch = '-';
2641                 } else {
2642                         os << "  ";
2643                         ch = ' ';
2644                 }
2645                 col_type column = cellColumn(i);
2646                 int len = clen[column];
2647                 while (column < columnCount() -1
2648                        && isPartOfMultiColumn(row, ++column))
2649                         len += clen[column] + 4;
2650                 os << docstring(len, ch);
2651                 if (bottomLine(i)) {
2652                         if (rightLine(i))
2653                                 os << "-+";
2654                         else
2655                                 os << "--";
2656                 } else {
2657                         os << "  ";
2658                 }
2659         }
2660         os << endl;
2661         return true;
2662 }
2663
2664
2665 void Tabular::plaintextPrintCell(Buffer const & buf, odocstream & os,
2666                                OutputParams const & runparams,
2667                                idx_type cell, row_type row, col_type column,
2668                                vector<unsigned int> const & clen,
2669                                bool onlydata) const
2670 {
2671         odocstringstream sstr;
2672         getCellInset(cell)->plaintext(buf, sstr, runparams);
2673
2674         if (onlydata) {
2675                 os << sstr.str();
2676                 return;
2677         }
2678
2679         if (leftLine(cell))
2680                 os << "| ";
2681         else
2682                 os << "  ";
2683
2684         unsigned int len1 = sstr.str().length();
2685         unsigned int len2 = clen[column];
2686         while (column < columnCount() -1
2687                && isPartOfMultiColumn(row, ++column))
2688                 len2 += clen[column] + 4;
2689         len2 -= len1;
2690
2691         switch (getAlignment(cell)) {
2692         default:
2693         case LYX_ALIGN_LEFT:
2694                 len1 = 0;
2695                 break;
2696         case LYX_ALIGN_RIGHT:
2697                 len1 = len2;
2698                 len2 = 0;
2699                 break;
2700         case LYX_ALIGN_CENTER:
2701                 len1 = len2 / 2;
2702                 len2 -= len1;
2703                 break;
2704         }
2705
2706         os << docstring(len1, ' ') << sstr.str()
2707            << docstring(len2, ' ');
2708
2709         if (rightLine(cell))
2710                 os << " |";
2711         else
2712                 os << "  ";
2713 }
2714
2715
2716 void Tabular::plaintext(Buffer const & buf, odocstream & os,
2717                            OutputParams const & runparams, int const depth,
2718                            bool onlydata, char_type delim) const
2719 {
2720         // first calculate the width of the single columns
2721         vector<unsigned int> clen(columnCount());
2722
2723         if (!onlydata) {
2724                 // first all non (real) multicolumn cells!
2725                 for (col_type j = 0; j < columnCount(); ++j) {
2726                         clen[j] = 0;
2727                         for (row_type i = 0; i < rowCount(); ++i) {
2728                                 idx_type cell = cellIndex(i, j);
2729                                 if (isMultiColumnReal(cell))
2730                                         continue;
2731                                 odocstringstream sstr;
2732                                 getCellInset(cell)->plaintext(buf, sstr, runparams);
2733                                 if (clen[j] < sstr.str().length())
2734                                         clen[j] = sstr.str().length();
2735                         }
2736                 }
2737                 // then all (real) multicolumn cells!
2738                 for (col_type j = 0; j < columnCount(); ++j) {
2739                         for (row_type i = 0; i < rowCount(); ++i) {
2740                                 idx_type cell = cellIndex(i, j);
2741                                 if (!isMultiColumnReal(cell) || isPartOfMultiColumn(i, j))
2742                                         continue;
2743                                 odocstringstream sstr;
2744                                 getCellInset(cell)->plaintext(buf, sstr, runparams);
2745                                 int len = int(sstr.str().length());
2746                                 idx_type const n = cells_in_multicolumn(cell);
2747                                 for (col_type k = j; len > 0 && k < j + n - 1; ++k)
2748                                         len -= clen[k];
2749                                 if (len > int(clen[j + n - 1]))
2750                                         clen[j + n - 1] = len;
2751                         }
2752                 }
2753         }
2754         idx_type cell = 0;
2755         for (row_type i = 0; i < rowCount(); ++i) {
2756                 if (!onlydata && plaintextTopHLine(os, i, clen))
2757                         os << docstring(depth * 2, ' ');
2758                 for (col_type j = 0; j < columnCount(); ++j) {
2759                         if (isPartOfMultiColumn(i, j))
2760                                 continue;
2761                         if (onlydata && j > 0)
2762                                 // we don't use operator<< for single UCS4 character.
2763                                 // see explanation in docstream.h
2764                                 os.put(delim);
2765                         plaintextPrintCell(buf, os, runparams,
2766                                            cell, i, j, clen, onlydata);
2767                         ++cell;
2768                 }
2769                 os << endl;
2770                 if (!onlydata) {
2771                         os << docstring(depth * 2, ' ');
2772                         if (plaintextBottomHLine(os, i, clen))
2773                                 os << docstring(depth * 2, ' ');
2774                 }
2775         }
2776 }
2777
2778
2779 shared_ptr<InsetText> Tabular::getCellInset(idx_type cell) const
2780 {
2781         return cell_info[cellRow(cell)][cellColumn(cell)].inset;
2782 }
2783
2784
2785 shared_ptr<InsetText> Tabular::getCellInset(row_type row,
2786                                                col_type column) const
2787 {
2788         return cell_info[row][column].inset;
2789 }
2790
2791
2792 void Tabular::setCellInset(row_type row, col_type column,
2793                               shared_ptr<InsetText> ins) const
2794 {
2795         cell_info[row][column].inset = ins;
2796 }
2797
2798
2799 Tabular::idx_type
2800 Tabular::getCellFromInset(Inset const * inset) const
2801 {
2802         // is this inset part of the tabular?
2803         if (!inset) {
2804                 lyxerr << "Error: this is not a cell of the tabular!" << endl;
2805                 BOOST_ASSERT(false);
2806         }
2807
2808         for (idx_type cell = 0, n = cellCount(); cell < n; ++cell)
2809                 if (getCellInset(cell).get() == inset) {
2810                         LYXERR(Debug::INSETTEXT, "Tabular::getCellFromInset: "
2811                                 << "cell=" << cell);
2812                         return cell;
2813                 }
2814
2815         // We should have found a cell at this point
2816         lyxerr << "Tabular::getCellFromInset: Cell of inset "
2817                 << inset << " not found!" << endl;
2818         BOOST_ASSERT(false);
2819         // shut up compiler
2820         return 0;
2821 }
2822
2823
2824 void Tabular::validate(LaTeXFeatures & features) const
2825 {
2826         features.require("NeedTabularnewline");
2827         if (useBookTabs())
2828                 features.require("booktabs");
2829         if (isLongTabular())
2830                 features.require("longtable");
2831         if (needRotating())
2832                 features.require("rotating");
2833         for (idx_type cell = 0; cell < cellCount(); ++cell) {
2834                 if (getVAlignment(cell) != LYX_VALIGN_TOP ||
2835                      (!getPWidth(cell).zero() && !isMultiColumn(cell)))
2836                         features.require("array");
2837                 getCellInset(cell)->validate(features);
2838         }
2839 }
2840
2841
2842 Tabular::BoxType Tabular::useParbox(idx_type cell) const
2843 {
2844         ParagraphList const & parlist = getCellInset(cell)->paragraphs();
2845         ParagraphList::const_iterator cit = parlist.begin();
2846         ParagraphList::const_iterator end = parlist.end();
2847
2848         for (; cit != end; ++cit)
2849                 for (int i = 0; i < cit->size(); ++i)
2850                         if (cit->isNewline(i))
2851                                 return BOX_PARBOX;
2852
2853         return BOX_NONE;
2854 }
2855
2856
2857 /////////////////////////////////////////////////////////////////////
2858 //
2859 // InsetTabular
2860 //
2861 /////////////////////////////////////////////////////////////////////
2862
2863 InsetTabular::InsetTabular(Buffer const & buf, row_type rows,
2864                            col_type columns)
2865         : tabular(buf.params(), max(rows, row_type(1)),
2866           max(columns, col_type(1))), buffer_(&buf), scx_(0)
2867 {}
2868
2869
2870 InsetTabular::InsetTabular(InsetTabular const & tab)
2871         : Inset(tab), tabular(tab.tabular),
2872                 buffer_(tab.buffer_), scx_(0)
2873 {}
2874
2875
2876 InsetTabular::~InsetTabular()
2877 {
2878         InsetTabularMailer(*this).hideDialog();
2879 }
2880
2881
2882 Inset * InsetTabular::clone() const
2883 {
2884         return new InsetTabular(*this);
2885 }
2886
2887
2888 Buffer const & InsetTabular::buffer() const
2889 {
2890         return *buffer_;
2891 }
2892
2893
2894 void InsetTabular::buffer(Buffer const * b)
2895 {
2896         buffer_ = b;
2897 }
2898
2899
2900 void InsetTabular::write(Buffer const & buf, ostream & os) const
2901 {
2902         os << "Tabular" << endl;
2903         tabular.write(buf, os);
2904 }
2905
2906
2907 void InsetTabular::read(Buffer const & buf, Lexer & lex)
2908 {
2909         bool const old_format = (lex.getString() == "\\LyXTable");
2910
2911         tabular.read(buf, lex);
2912
2913         if (old_format)
2914                 return;
2915
2916         lex.next();
2917         string token = lex.getString();
2918         while (lex && token != "\\end_inset") {
2919                 lex.next();
2920                 token = lex.getString();
2921         }
2922         if (!lex) {
2923                 lex.printError("Missing \\end_inset at this point. "
2924                                "Read: `$$Token'");
2925         }
2926 }
2927
2928
2929 void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
2930 {
2931         //lyxerr << "InsetTabular::metrics: " << mi.base.bv << " width: " <<
2932         //      mi.base.textwidth << "\n";
2933         if (!mi.base.bv) {
2934                 lyxerr << "InsetTabular::metrics: need bv" << endl;
2935                 BOOST_ASSERT(false);
2936         }
2937
2938         row_type i = 0;
2939         for (idx_type cell = 0; i < tabular.rowCount(); ++i) {
2940                 int maxAsc = 0;
2941                 int maxDesc = 0;
2942                 for (col_type j = 0; j < tabular.columnCount(); ++j) {
2943                         if (tabular.isPartOfMultiColumn(i, j))
2944                                 // Multicolumn cell, but not first one
2945                                 continue;
2946                         Dimension dim;
2947                         MetricsInfo m = mi;
2948                         Length p_width;
2949                         if (tabular.cell_info[i][j].multicolumn ==
2950                             Tabular::CELL_BEGIN_OF_MULTICOLUMN)
2951                                 p_width = tabular.cellinfo_of_cell(cell).p_width;
2952                         else
2953                                 p_width = tabular.column_info[j].p_width;
2954                         if (!p_width.zero())
2955                                 m.base.textwidth = p_width.inPixels(mi.base.textwidth);
2956                         tabular.getCellInset(cell)->metrics(m, dim);
2957                         if (!p_width.zero())
2958                                 dim.wid = m.base.textwidth;
2959                         tabular.setCellWidth(cell, dim.wid);
2960                         if (p_width.zero()) {
2961                                 m.base.textwidth = dim.wid + 2 * ADD_TO_TABULAR_WIDTH;
2962                                 // FIXME there must be a way to get rid of
2963                                 // the second metrics call
2964                                 tabular.getCellInset(cell)->metrics(m, dim);
2965                         }
2966                         maxAsc  = max(maxAsc, dim.asc);
2967                         maxDesc = max(maxDesc, dim.des);
2968                         ++cell;
2969                 }
2970                 int const top_space = tabular.row_info[i].top_space_default ?
2971                         default_line_space :
2972                         tabular.row_info[i].top_space.inPixels(mi.base.textwidth);
2973                 tabular.setRowAscent(i, maxAsc + ADD_TO_HEIGHT + top_space);
2974                 int const bottom_space = tabular.row_info[i].bottom_space_default ?
2975                         default_line_space :
2976                         tabular.row_info[i].bottom_space.inPixels(mi.base.textwidth);
2977                 tabular.setRowDescent(i, maxDesc + ADD_TO_HEIGHT + bottom_space);
2978         }
2979
2980         dim.asc = tabular.rowAscent(0);
2981         dim.des = tabular.height() - dim.asc;
2982         dim.wid = tabular.width() + 2 * ADD_TO_TABULAR_WIDTH;
2983 }
2984
2985
2986 void InsetTabular::draw(PainterInfo & pi, int x, int y) const
2987 {
2988         //lyxerr << "InsetTabular::draw: " << x << " " << y << endl;
2989         BufferView * bv = pi.base.bv;
2990
2991         // FIXME: As the full backrgound is painted in drawSelection(),
2992         // we have no choice but to do a full repaint for the Text cells.
2993         pi.full_repaint = true;
2994
2995         resetPos(bv->cursor());
2996
2997         x += scx_;
2998         x += ADD_TO_TABULAR_WIDTH;
2999
3000         bool const original_drawing_state = pi.pain.isDrawingEnabled();
3001
3002         idx_type idx = 0;
3003         first_visible_cell = Tabular::npos;
3004         for (row_type i = 0; i < tabular.rowCount(); ++i) {
3005                 int nx = x;
3006                 int const a = tabular.rowAscent(i);
3007                 int const d = tabular.rowDescent(i);
3008                 idx = tabular.cellIndex(i, 0);
3009                 for (col_type j = 0; j < tabular.columnCount(); ++j) {
3010                         if (tabular.isPartOfMultiColumn(i, j))
3011                                 continue;
3012                         if (first_visible_cell == Tabular::npos)
3013                                 first_visible_cell = idx;
3014
3015                         int const cx = nx + tabular.getBeginningOfTextInCell(idx);
3016                         // Cache the Inset position.
3017                         bv->coordCache().insets().add(cell(idx).get(), cx, y);
3018                         if (nx + tabular.columnWidth(idx) < 0
3019                             || nx > bv->workWidth()
3020                             || y + d < 0
3021                             || y - a > bv->workHeight()) {
3022                                 pi.pain.setDrawingEnabled(false);
3023                                 cell(idx)->draw(pi, cx, y);
3024                                 drawCellLines(pi.pain, nx, y, i, idx, pi.erased_);
3025                                 pi.pain.setDrawingEnabled(original_drawing_state);
3026                         } else {
3027                                 cell(idx)->draw(pi, cx, y);
3028                                 drawCellLines(pi.pain, nx, y, i, idx, pi.erased_);
3029                         }
3030                         nx += tabular.columnWidth(idx);
3031                         ++idx;
3032                 }
3033
3034                 if (i + 1 < tabular.rowCount())
3035                         y += d + tabular.rowAscent(i + 1) +
3036                                 tabular.getAdditionalHeight(i + 1);
3037         }
3038 }
3039
3040
3041 void InsetTabular::drawSelection(PainterInfo & pi, int x, int y) const
3042 {
3043         Cursor & cur = pi.base.bv->cursor();
3044
3045         x += scx_ + ADD_TO_TABULAR_WIDTH;
3046
3047         // FIXME: it is wrong to completely paint the background
3048         // if we want to do single row painting.
3049
3050         // Paint background of current tabular
3051         int const w = tabular.width();
3052         int const h = tabular.height();
3053         int yy = y - tabular.rowAscent(0);
3054         pi.pain.fillRectangle(x, yy, w, h, backgroundColor());
3055
3056         if (!cur.selection())
3057                 return;
3058         if (&cur.inset() != this)
3059                 return;
3060
3061         //resetPos(cur);
3062
3063
3064         if (tablemode(cur)) {
3065                 row_type rs, re;
3066                 col_type cs, ce;
3067                 getSelection(cur, rs, re, cs, ce);
3068                 y -= tabular.rowAscent(0);
3069                 for (row_type j = 0; j < tabular.rowCount(); ++j) {
3070                         int const a = tabular.rowAscent(j);
3071                         int const h = a + tabular.rowDescent(j);
3072                         int xx = x;
3073                         y += tabular.getAdditionalHeight(j);
3074                         for (col_type i = 0; i < tabular.columnCount(); ++i) {
3075                                 if (tabular.isPartOfMultiColumn(j, i))
3076                                         continue;
3077                                 idx_type const cell =
3078                                         tabular.cellIndex(j, i);
3079                                 int const w = tabular.columnWidth(cell);
3080                                 if (i >= cs && i <= ce && j >= rs && j <= re)
3081                                         pi.pain.fillRectangle(xx, y, w, h,
3082                                                               Color_selection);
3083                                 xx += w;
3084                         }
3085                         y += h;
3086                 }
3087
3088         } else {
3089                 x += getCellXPos(cur.idx());
3090                 x += tabular.getBeginningOfTextInCell(cur.idx());
3091                 cell(cur.idx())->drawSelection(pi, x, 0 /* ignored */);
3092         }
3093 }
3094
3095
3096 void InsetTabular::drawCellLines(Painter & pain, int x, int y,
3097                                  row_type row, idx_type cell, bool erased) const
3098 {
3099         int x2 = x + tabular.columnWidth(cell);
3100         bool on_off = false;
3101         ColorCode col = Color_tabularline;
3102         ColorCode onoffcol = Color_tabularonoffline;
3103
3104         if (erased) {
3105                 col = Color_deletedtext;
3106                 onoffcol = Color_deletedtext;
3107         }
3108
3109         if (!tabular.topAlreadyDrawn(cell)) {
3110                 on_off = !tabular.topLine(cell);
3111                 pain.line(x, y - tabular.rowAscent(row),
3112                           x2, y -  tabular.rowAscent(row),
3113                           on_off ? onoffcol : col,
3114                           on_off ? Painter::line_onoffdash : Painter::line_solid);
3115         }
3116         on_off = !tabular.bottomLine(cell);
3117         pain.line(x, y + tabular.rowDescent(row),
3118                   x2, y + tabular.rowDescent(row),
3119                   on_off ? onoffcol : col,
3120                   on_off ? Painter::line_onoffdash : Painter::line_solid);
3121         if (!tabular.leftAlreadyDrawn(cell)) {
3122                 on_off = !tabular.leftLine(cell);
3123                 pain.line(x, y -  tabular.rowAscent(row),
3124                           x, y +  tabular.rowDescent(row),
3125                           on_off ? onoffcol : col,
3126                           on_off ? Painter::line_onoffdash : Painter::line_solid);
3127         }
3128         on_off = !tabular.rightLine(cell);
3129         pain.line(x2 - tabular.getAdditionalWidth(cell),
3130                   y -  tabular.rowAscent(row),
3131                   x2 - tabular.getAdditionalWidth(cell),
3132                   y +  tabular.rowDescent(row),
3133                   on_off ? onoffcol : col,
3134                   on_off ? Painter::line_onoffdash : Painter::line_solid);
3135 }
3136
3137
3138 docstring const InsetTabular::editMessage() const
3139 {
3140         return _("Opened table");
3141 }
3142
3143
3144 void InsetTabular::edit(Cursor & cur, bool left)
3145 {
3146         //lyxerr << "InsetTabular::edit: " << this << endl;
3147         cur.finishUndo();
3148         cur.selection() = false;
3149         cur.push(*this);
3150         if (left) {
3151                 if (isRightToLeft(cur))
3152                         cur.idx() = tabular.getLastCellInRow(0);
3153                 else
3154                         cur.idx() = 0;
3155                 cur.pit() = 0;
3156                 cur.pos() = 0;
3157         } else {
3158                 if (isRightToLeft(cur))
3159                         cur.idx() = tabular.getFirstCellInRow(tabular.rowCount() - 1);
3160                 else
3161                         cur.idx() = tabular.cellCount() - 1;
3162                 cur.pit() = 0;
3163                 cur.pos() = cur.lastpos(); // FIXME crude guess
3164         }
3165         // FIXME: this accesses the position cache before it is initialized
3166         //resetPos(cur);
3167         //cur.bv().fitCursor();
3168 }
3169
3170
3171 void InsetTabular::updateLabels(Buffer const & buf, ParIterator const & it)
3172 {
3173         // In a longtable, tell captions what the current float is
3174         Counters & cnts = buf.params().getTextClass().counters();
3175         string const saveflt = cnts.current_float();
3176         if (tabular.isLongTabular())
3177                 cnts.current_float("table");
3178
3179         ParIterator it2 = it;
3180         it2.forwardPos();
3181         size_t const end = it2.nargs();
3182         for ( ; it2.idx() < end; it2.top().forwardIdx())
3183                 lyx::updateLabels(buf, it2);
3184
3185         //reset afterwards
3186         if (tabular.isLongTabular())
3187                 cnts.current_float(saveflt);
3188 }
3189
3190
3191 void InsetTabular::doDispatch(Cursor & cur, FuncRequest & cmd)
3192 {
3193         LYXERR(Debug::DEBUG, "# InsetTabular::doDispatch: cmd: " << cmd
3194                              << "\n  cur:" << cur);
3195         CursorSlice sl = cur.top();
3196         Cursor & bvcur = cur.bv().cursor();
3197
3198         switch (cmd.action) {
3199
3200         case LFUN_MOUSE_PRESS:
3201                 //lyxerr << "# InsetTabular::MousePress\n" << cur.bv().cursor() << endl;
3202
3203                 // do not reset cursor/selection if we have selected
3204                 // some cells (bug 2715).
3205                 if (cmd.button() == mouse_button::button3
3206                     && &bvcur.selBegin().inset() == this 
3207                     && tablemode(bvcur)) 
3208                         ;
3209                 else
3210                         // Let InsetText do it
3211                         cell(cur.idx())->dispatch(cur, cmd);
3212                 break;
3213         case LFUN_MOUSE_MOTION:
3214                 //lyxerr << "# InsetTabular::MouseMotion\n" << bvcur << endl;
3215                 if (cmd.button() == mouse_button::button1) {
3216                         // only accept motions to places not deeper nested than the real anchor
3217                         if (bvcur.anchor_.hasPart(cur)) {
3218                                 // only update if selection changes
3219                                 if (bvcur.idx() == cur.idx() &&
3220                                         !(bvcur.anchor_.idx() == cur.idx() && bvcur.pos() != cur.pos()))
3221                                         cur.noUpdate();
3222                                 setCursorFromCoordinates(cur, cmd.x, cmd.y);
3223                                 bvcur.setCursor(cur);
3224                                 bvcur.selection() = true;
3225                         } else
3226                                 cur.undispatched();
3227                 }
3228                 break;
3229
3230         case LFUN_MOUSE_RELEASE:
3231                 //lyxerr << "# InsetTabular::MouseRelease\n" << bvcur << endl;
3232                 if (cmd.button() == mouse_button::button3)
3233                         InsetTabularMailer(*this).showDialog(&cur.bv());
3234                 break;
3235
3236         case LFUN_CELL_BACKWARD:
3237                 movePrevCell(cur);
3238                 cur.selection() = false;
3239                 break;
3240
3241         case LFUN_CELL_FORWARD:
3242                 moveNextCell(cur);
3243                 cur.selection() = false;
3244                 break;
3245
3246         case LFUN_CHAR_FORWARD_SELECT:
3247         case LFUN_CHAR_FORWARD:
3248                 cell(cur.idx())->dispatch(cur, cmd);
3249                 if (!cur.result().dispatched()) {
3250                         moveNextCell(cur);
3251                         if (sl == cur.top())
3252                                 cmd = FuncRequest(LFUN_FINISHED_FORWARD);
3253                         else
3254                                 cur.dispatched();
3255                 }
3256                 break;
3257
3258         case LFUN_CHAR_BACKWARD_SELECT:
3259         case LFUN_CHAR_BACKWARD:
3260                 cell(cur.idx())->dispatch(cur, cmd);
3261                 if (!cur.result().dispatched()) {
3262                         movePrevCell(cur);
3263                         if (sl == cur.top())
3264                                 cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
3265                         else
3266                                 cur.dispatched();
3267                 }
3268                 break;
3269
3270         case LFUN_CHAR_RIGHT_SELECT:
3271         case LFUN_CHAR_RIGHT:
3272                 //FIXME: for visual cursor, really move right
3273                 if (isRightToLeft(cur))
3274                         lyx::dispatch(FuncRequest(
3275                                 cmd.action == LFUN_CHAR_RIGHT_SELECT ?
3276                                         LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD));
3277                 else
3278                         lyx::dispatch(FuncRequest(
3279                                 cmd.action == LFUN_CHAR_RIGHT_SELECT ?
3280                                         LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD));
3281                 break;
3282
3283         case LFUN_CHAR_LEFT_SELECT:
3284         case LFUN_CHAR_LEFT:
3285                 //FIXME: for visual cursor, really move left
3286                 if (isRightToLeft(cur))
3287                         lyx::dispatch(FuncRequest(
3288                                 cmd.action == LFUN_CHAR_LEFT_SELECT ?
3289                                         LFUN_CHAR_FORWARD_SELECT : LFUN_CHAR_FORWARD));
3290                 else
3291                         lyx::dispatch(FuncRequest(
3292                                 cmd.action == LFUN_CHAR_LEFT_SELECT ?
3293                                         LFUN_CHAR_BACKWARD_SELECT : LFUN_CHAR_BACKWARD));
3294                 break;
3295
3296         case LFUN_DOWN_SELECT:
3297         case LFUN_DOWN:
3298                 cell(cur.idx())->dispatch(cur, cmd);
3299                 cur.dispatched(); // override the cell's decision
3300                 if (sl == cur.top())
3301                         // if our Text didn't do anything to the cursor
3302                         // then we try to put the cursor into the cell below
3303                         // setting also the right targetX.
3304                         if (tabular.cellRow(cur.idx()) != tabular.rowCount() - 1) {
3305                                 cur.idx() = tabular.getCellBelow(cur.idx());
3306                                 cur.pit() = 0;
3307                                 TextMetrics const & tm =
3308                                         cur.bv().textMetrics(cell(cur.idx())->getText(0));
3309                                 cur.pos() = tm.x2pos(cur.pit(), 0, cur.targetX());
3310                         }
3311                 if (sl == cur.top()) {
3312                         // we trick it to go to forward after leaving the
3313                         // tabular.
3314                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
3315                         cur.undispatched();
3316                 }
3317                 break;
3318
3319         case LFUN_UP_SELECT:
3320         case LFUN_UP:
3321                 cell(cur.idx())->dispatch(cur, cmd);
3322                 cur.dispatched(); // override the cell's decision
3323                 if (sl == cur.top())
3324                         // if our Text didn't do anything to the cursor
3325                         // then we try to put the cursor into the cell above
3326                         // setting also the right targetX.
3327                         if (tabular.cellRow(cur.idx()) != 0) {
3328                                 cur.idx() = tabular.getCellAbove(cur.idx());
3329                                 cur.pit() = cur.lastpit();
3330                                 Text const * text = cell(cur.idx())->getText(0);
3331                                 TextMetrics const & tm = cur.bv().textMetrics(text);
3332                                 ParagraphMetrics const & pm =
3333                                         tm.parMetrics(cur.lastpit());
3334                                 cur.pos() = tm.x2pos(cur.pit(), pm.rows().size()-1, cur.targetX());
3335                         }
3336                 if (sl == cur.top()) {
3337                         cmd = FuncRequest(LFUN_UP);
3338                         cur.undispatched();
3339                 }
3340                 break;
3341
3342 //      case LFUN_SCREEN_DOWN: {
3343 //              //if (hasSelection())
3344 //              //      cur.selection() = false;
3345 //              col_type const col = tabular.cellColumn(cur.idx());
3346 //              int const t =   cur.bv().top_y() + cur.bv().height();
3347 //              if (t < yo() + tabular.getHeightOfTabular()) {
3348 //                      cur.bv().scrollDocView(t);
3349 //                      cur.idx() = tabular.getCellBelow(first_visible_cell) + col;
3350 //              } else {
3351 //                      cur.idx() = tabular.getFirstCellInRow(tabular.rows() - 1) + col;
3352 //              }
3353 //              cur.par() = 0;
3354 //              cur.pos() = 0;
3355 //              break;
3356 //      }
3357 //
3358 //      case LFUN_SCREEN_UP: {
3359 //              //if (hasSelection())
3360 //              //      cur.selection() = false;
3361 //              col_type const col = tabular.cellColumn(cur.idx());
3362 //              int const t =   cur.bv().top_y() + cur.bv().height();
3363 //              if (yo() < 0) {
3364 //                      cur.bv().scrollDocView(t);
3365 //                      if (yo() > 0)
3366 //                              cur.idx() = col;
3367 //                      else
3368 //                              cur.idx() = tabular.getCellBelow(first_visible_cell) + col;
3369 //              } else {
3370 //                      cur.idx() = col;
3371 //              }
3372 //              cur.par() = cur.lastpar();
3373 //              cur.pos() = cur.lastpos();
3374 //              break;
3375 //      }
3376
3377         case LFUN_LAYOUT_TABULAR:
3378                 InsetTabularMailer(*this).showDialog(&cur.bv());
3379                 break;
3380
3381         case LFUN_INSET_DIALOG_UPDATE:
3382                 InsetTabularMailer(*this).updateDialog(&cur.bv());
3383                 break;
3384
3385         case LFUN_TABULAR_FEATURE:
3386                 if (!tabularFeatures(cur, to_utf8(cmd.argument())))
3387                         cur.undispatched();
3388                 break;
3389
3390         // insert file functions
3391         case LFUN_FILE_INSERT_PLAINTEXT_PARA:
3392         case LFUN_FILE_INSERT_PLAINTEXT: {
3393                 // FIXME UNICODE
3394                 docstring const tmpstr = cur.bv().contentsOfPlaintextFile(
3395                         FileName(to_utf8(cmd.argument())));
3396                 if (tmpstr.empty())
3397                         break;
3398                 cur.recordUndoInset(INSERT_UNDO);
3399                 if (insertPlaintextString(cur.bv(), tmpstr, false)) {
3400                         // content has been replaced,
3401                         // so cursor might be invalid
3402                         cur.pos() = cur.lastpos();
3403                         cur.pit() = cur.lastpit();
3404                         bvcur.setCursor(cur);
3405                 } else
3406                         cur.undispatched();
3407                 break;
3408         }
3409
3410         case LFUN_CUT:
3411                 if (tablemode(cur)) {
3412                         if (copySelection(cur)) {
3413                                 cur.recordUndoInset(DELETE_UNDO);
3414                                 cutSelection(cur);
3415                         }
3416                 }
3417                 else
3418                         cell(cur.idx())->dispatch(cur, cmd);
3419                 break;
3420
3421         case LFUN_CHAR_DELETE_BACKWARD:
3422         case LFUN_CHAR_DELETE_FORWARD:
3423                 if (tablemode(cur)) {
3424                         cur.recordUndoInset(DELETE_UNDO);
3425                         cutSelection(cur);
3426                 }
3427                 else
3428                         cell(cur.idx())->dispatch(cur, cmd);
3429                 break;
3430
3431         case LFUN_COPY:
3432                 if (!cur.selection())
3433                         break;
3434                 if (tablemode(cur)) {
3435                         cur.finishUndo();
3436                         copySelection(cur);
3437                 } else
3438                         cell(cur.idx())->dispatch(cur, cmd);
3439                 break;
3440
3441         case LFUN_CLIPBOARD_PASTE:
3442         case LFUN_PRIMARY_SELECTION_PASTE: {
3443                 docstring const clip = (cmd.action == LFUN_CLIPBOARD_PASTE) ?
3444                         theClipboard().getAsText() :
3445                         theSelection().get();
3446                 if (clip.empty())
3447                         break;
3448                 // pass to InsertPlaintextString, but
3449                 // only if we have multi-cell content
3450                 if (clip.find_first_of(from_ascii("\t\n")) != docstring::npos) {
3451                         cur.recordUndoInset(INSERT_UNDO);
3452                         if (insertPlaintextString(cur.bv(), clip, false)) {
3453                                 // content has been replaced,
3454                                 // so cursor might be invalid
3455                                 cur.pos() = cur.lastpos();
3456                                 cur.pit() = cur.lastpit();
3457                                 bvcur.setCursor(cur);
3458                                 break;
3459                         }
3460                 }
3461                 // Let the cell handle normal text
3462                 cell(cur.idx())->dispatch(cur, cmd);
3463                 break;
3464         }
3465
3466         case LFUN_PASTE:
3467                 if (tabularStackDirty() && theClipboard().isInternal()) {
3468                         cur.recordUndoInset(INSERT_UNDO);
3469                         pasteClipboard(cur);
3470                         break;
3471                 }
3472                 cell(cur.idx())->dispatch(cur, cmd);
3473                 break;
3474
3475         case LFUN_FONT_EMPH:
3476         case LFUN_FONT_BOLD:
3477         case LFUN_FONT_ROMAN:
3478         case LFUN_FONT_NOUN:
3479         case LFUN_FONT_ITAL:
3480         case LFUN_FONT_FRAK:
3481         case LFUN_FONT_TYPEWRITER:
3482         case LFUN_FONT_SANS:
3483         case LFUN_FONT_FREE_APPLY:
3484         case LFUN_FONT_FREE_UPDATE:
3485         case LFUN_FONT_SIZE:
3486         case LFUN_FONT_UNDERLINE:
3487         case LFUN_LANGUAGE:
3488         case LFUN_WORD_CAPITALIZE:
3489         case LFUN_WORD_UPCASE:
3490         case LFUN_WORD_LOWCASE:
3491         case LFUN_CHARS_TRANSPOSE:
3492                 if (tablemode(cur)) {
3493                         row_type rs, re;
3494                         col_type cs, ce;
3495                         getSelection(cur, rs, re, cs, ce);
3496                         Cursor tmpcur = cur;
3497                         for (row_type i = rs; i <= re; ++i) {
3498                                 for (col_type j = cs; j <= ce; ++j) {
3499                                         // cursor follows cell:
3500                                         tmpcur.idx() = tabular.cellIndex(i, j);
3501                                         // select this cell only:
3502                                         tmpcur.pit() = 0;
3503                                         tmpcur.pos() = 0;
3504                                         tmpcur.resetAnchor();
3505                                         tmpcur.pit() = tmpcur.lastpit();
3506                                         tmpcur.pos() = tmpcur.top().lastpos();
3507                                         tmpcur.setCursor(tmpcur);
3508                                         tmpcur.setSelection();
3509                                         cell(tmpcur.idx())->dispatch(tmpcur, cmd);
3510                                 }
3511                         }
3512                         break;
3513                 } else {
3514                         cell(cur.idx())->dispatch(cur, cmd);
3515                         break;
3516                 }
3517         default:
3518                 // we try to handle this event in the insets dispatch function.
3519                 cell(cur.idx())->dispatch(cur, cmd);
3520                 break;
3521         }
3522 }
3523
3524
3525 // function sets an object as defined in func_status.h:
3526 // states OK, Unknown, Disabled, On, Off.
3527 bool InsetTabular::getStatus(Cursor & cur, FuncRequest const & cmd,
3528         FuncStatus & status) const
3529 {
3530         switch (cmd.action) {
3531         case LFUN_TABULAR_FEATURE: {
3532                 int action = Tabular::LAST_ACTION;
3533                 int i = 0;
3534                 for (; tabularFeature[i].action != Tabular::LAST_ACTION; ++i) {
3535                         string const tmp = tabularFeature[i].feature;
3536                         if (tmp == to_utf8(cmd.argument()).substr(0, tmp.length())) {
3537                                 action = tabularFeature[i].action;
3538                                 break;
3539                         }
3540                 }
3541                 if (action == Tabular::LAST_ACTION) {
3542                         status.clear();
3543                         status.unknown(true);
3544                         return true;
3545                 }
3546
3547                 string const argument
3548                         = ltrim(to_utf8(cmd.argument()).substr(tabularFeature[i].feature.length()));
3549
3550                 row_type sel_row_start = 0;
3551                 row_type sel_row_end = 0;
3552                 col_type sel_col_start = 0;
3553                 col_type sel_col_end = 0;
3554                 Tabular::ltType dummyltt;
3555                 bool flag = true;
3556
3557                 getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
3558
3559                 switch (action) {
3560                 case Tabular::SET_PWIDTH:
3561                 case Tabular::SET_MPWIDTH:
3562                 case Tabular::SET_SPECIAL_COLUMN:
3563                 case Tabular::SET_SPECIAL_MULTI:
3564                 case Tabular::APPEND_ROW:
3565                 case Tabular::APPEND_COLUMN:
3566                 case Tabular::DELETE_ROW:
3567                 case Tabular::DELETE_COLUMN:
3568                 case Tabular::COPY_ROW:
3569                 case Tabular::COPY_COLUMN:
3570                 case Tabular::SET_ALL_LINES:
3571                 case Tabular::UNSET_ALL_LINES:
3572                 case Tabular::SET_TOP_SPACE:
3573                 case Tabular::SET_BOTTOM_SPACE:
3574                 case Tabular::SET_INTERLINE_SPACE:
3575                         status.clear();
3576                         return true;
3577
3578                 case Tabular::MULTICOLUMN:
3579                         status.setOnOff(tabular.isMultiColumn(cur.idx()));
3580                         break;
3581
3582                 case Tabular::M_TOGGLE_LINE_TOP:
3583                         flag = false;
3584                 case Tabular::TOGGLE_LINE_TOP:
3585                         status.setOnOff(tabular.topLine(cur.idx(), flag));
3586                         break;
3587
3588                 case Tabular::M_TOGGLE_LINE_BOTTOM:
3589                         flag = false;
3590                 case Tabular::TOGGLE_LINE_BOTTOM:
3591                         status.setOnOff(tabular.bottomLine(cur.idx(), flag));
3592                         break;
3593
3594                 case Tabular::M_TOGGLE_LINE_LEFT:
3595                         flag = false;
3596                 case Tabular::TOGGLE_LINE_LEFT:
3597                         status.setOnOff(tabular.leftLine(cur.idx(), flag));
3598                         break;
3599
3600                 case Tabular::M_TOGGLE_LINE_RIGHT:
3601                         flag = false;
3602                 case Tabular::TOGGLE_LINE_RIGHT:
3603                         status.setOnOff(tabular.rightLine(cur.idx(), flag));
3604                         break;
3605
3606                 case Tabular::M_ALIGN_LEFT:
3607                         flag = false;
3608                 case Tabular::ALIGN_LEFT:
3609                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_LEFT);
3610                         break;
3611
3612                 case Tabular::M_ALIGN_RIGHT:
3613                         flag = false;
3614                 case Tabular::ALIGN_RIGHT:
3615                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_RIGHT);
3616                         break;
3617
3618                 case Tabular::M_ALIGN_CENTER:
3619                         flag = false;
3620                 case Tabular::ALIGN_CENTER:
3621                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_CENTER);
3622                         break;
3623
3624                 case Tabular::ALIGN_BLOCK:
3625                         status.enabled(!tabular.getPWidth(cur.idx()).zero());
3626                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_BLOCK);
3627                         break;
3628
3629                 case Tabular::M_VALIGN_TOP:
3630                         flag = false;
3631                 case Tabular::VALIGN_TOP:
3632                         status.setOnOff(
3633                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_TOP);
3634                         break;
3635
3636                 case Tabular::M_VALIGN_BOTTOM:
3637                         flag = false;
3638                 case Tabular::VALIGN_BOTTOM:
3639                         status.setOnOff(
3640                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_BOTTOM);
3641                         break;
3642
3643                 case Tabular::M_VALIGN_MIDDLE:
3644                         flag = false;
3645                 case Tabular::VALIGN_MIDDLE:
3646                         status.setOnOff(
3647                                 tabular.getVAlignment(cur.idx(), flag) == Tabular::LYX_VALIGN_MIDDLE);
3648                         break;
3649
3650                 case Tabular::SET_LONGTABULAR:
3651                         status.setOnOff(tabular.isLongTabular());
3652                         break;
3653
3654                 case Tabular::UNSET_LONGTABULAR:
3655                         status.setOnOff(!tabular.isLongTabular());
3656                         break;
3657
3658                 case Tabular::TOGGLE_ROTATE_TABULAR:
3659                 case Tabular::SET_ROTATE_TABULAR:
3660                         status.setOnOff(tabular.getRotateTabular());
3661                         break;
3662
3663                 case Tabular::UNSET_ROTATE_TABULAR:
3664                         status.setOnOff(!tabular.getRotateTabular());
3665                         break;
3666
3667                 case Tabular::TOGGLE_ROTATE_CELL:
3668                 case Tabular::SET_ROTATE_CELL:
3669                         status.setOnOff(!oneCellHasRotationState(false,
3670                                 sel_row_start, sel_row_end, sel_col_start, sel_col_end));
3671                         break;
3672
3673                 case Tabular::UNSET_ROTATE_CELL:
3674                         status.setOnOff(!oneCellHasRotationState(true,
3675                                 sel_row_start, sel_row_end, sel_col_start, sel_col_end));
3676                         break;
3677
3678                 case Tabular::SET_USEBOX:
3679                         status.setOnOff(convert<int>(argument) == tabular.getUsebox(cur.idx()));
3680                         break;
3681
3682                 case Tabular::SET_LTFIRSTHEAD:
3683                         status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
3684                         break;
3685
3686                 case Tabular::UNSET_LTFIRSTHEAD:
3687                         status.setOnOff(!tabular.getRowOfLTHead(sel_row_start, dummyltt));
3688                         break;
3689
3690                 case Tabular::SET_LTHEAD:
3691                         status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
3692                         break;
3693
3694                 case Tabular::UNSET_LTHEAD:
3695                         status.setOnOff(!tabular.getRowOfLTHead(sel_row_start, dummyltt));
3696                         break;
3697
3698                 case Tabular::SET_LTFOOT:
3699                         status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
3700                         break;
3701
3702                 case Tabular::UNSET_LTFOOT:
3703                         status.setOnOff(!tabular.getRowOfLTFoot(sel_row_start, dummyltt));
3704                         break;
3705
3706                 case Tabular::SET_LTLASTFOOT:
3707                         status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
3708                         break;
3709
3710                 case Tabular::UNSET_LTLASTFOOT:
3711                         status.setOnOff(!tabular.getRowOfLTFoot(sel_row_start, dummyltt));
3712                         break;
3713
3714                 case Tabular::SET_LTNEWPAGE:
3715                         status.setOnOff(tabular.getLTNewPage(sel_row_start));
3716                         break;
3717
3718                 case Tabular::SET_BOOKTABS:
3719                         status.setOnOff(tabular.useBookTabs());
3720                         break;
3721
3722                 case Tabular::UNSET_BOOKTABS:
3723                         status.setOnOff(!tabular.useBookTabs());
3724                         break;
3725
3726                 default:
3727                         status.clear();
3728                         status.enabled(false);
3729                         break;
3730                 }
3731                 return true;
3732         }
3733
3734         // These are only enabled inside tabular
3735         case LFUN_CELL_BACKWARD:
3736         case LFUN_CELL_FORWARD:
3737                 status.enabled(true);
3738                 return true;
3739
3740         // disable these with multiple cells selected
3741         case LFUN_INSET_INSERT:
3742         case LFUN_TABULAR_INSERT:
3743         case LFUN_FLEX_INSERT:
3744         case LFUN_FLOAT_INSERT:
3745         case LFUN_FLOAT_WIDE_INSERT:
3746         case LFUN_FOOTNOTE_INSERT:
3747         case LFUN_MARGINALNOTE_INSERT:
3748         case LFUN_MATH_INSERT:
3749         case LFUN_MATH_MODE:
3750         case LFUN_MATH_MUTATE:
3751         case LFUN_MATH_DISPLAY:
3752         case LFUN_NOTE_INSERT:
3753         case LFUN_OPTIONAL_INSERT:
3754         case LFUN_BOX_INSERT:
3755         case LFUN_BRANCH_INSERT:
3756         case LFUN_WRAP_INSERT:
3757         case LFUN_ERT_INSERT: {
3758                 if (tablemode(cur)) {
3759                         status.enabled(false);
3760                         return true;
3761                 } else
3762                         return cell(cur.idx())->getStatus(cur, cmd, status);
3763         }
3764
3765         // disable in non-fixed-width cells
3766         case LFUN_NEW_LINE:
3767         case LFUN_BREAK_PARAGRAPH:
3768         case LFUN_BREAK_PARAGRAPH_SKIP: {
3769                 if (tabular.getPWidth(cur.idx()).zero()) {
3770                         status.enabled(false);
3771                         return true;
3772                 } else
3773                         return cell(cur.idx())->getStatus(cur, cmd, status);
3774         }
3775
3776         case LFUN_PASTE:
3777                 if (tabularStackDirty() && theClipboard().isInternal()) {
3778                         status.enabled(true);
3779                         return true;
3780                 } else
3781                         return cell(cur.idx())->getStatus(cur, cmd, status);
3782
3783         case LFUN_INSET_MODIFY:
3784                 if (insetCode(cmd.getArg(0)) == TABULAR_CODE) {
3785                         status.enabled(true);
3786                         return true;
3787                 }
3788                 // Fall through
3789
3790         default:
3791                 // we try to handle this event in the insets dispatch function.
3792                 return cell(cur.idx())->getStatus(cur, cmd, status);
3793         }
3794 }
3795
3796
3797 int InsetTabular::latex(Buffer const & buf, odocstream & os,
3798                         OutputParams const & runparams) const
3799 {
3800         return tabular.latex(buf, os, runparams);
3801 }
3802
3803
3804 int InsetTabular::plaintext(Buffer const & buf, odocstream & os,
3805                             OutputParams const & runparams) const
3806 {
3807         os << '\n'; // output table on a new line
3808         int const dp = runparams.linelen > 0 ? runparams.depth : 0;
3809         tabular.plaintext(buf, os, runparams, dp, false, 0);
3810         return PLAINTEXT_NEWLINE;
3811 }
3812
3813
3814 int InsetTabular::docbook(Buffer const & buf, odocstream & os,
3815                           OutputParams const & runparams) const
3816 {
3817         int ret = 0;
3818         Inset * master = 0;
3819
3820         // FIXME: Why not pass a proper DocIterator here?
3821 #if 0
3822         // if the table is inside a float it doesn't need the informaltable
3823         // wrapper. Search for it.
3824         for (master = owner(); master; master = master->owner())
3825                 if (master->lyxCode() == FLOAT_CODE)
3826                         break;
3827 #endif
3828
3829         if (!master) {
3830                 os << "<informaltable>";
3831                 ++ret;
3832         }
3833         ret += tabular.docbook(buf, os, runparams);
3834         if (!master) {
3835                 os << "</informaltable>";
3836                 ++ret;
3837         }
3838         return ret;
3839 }
3840
3841
3842 void InsetTabular::validate(LaTeXFeatures & features) const
3843 {
3844         tabular.validate(features);
3845 }
3846
3847
3848 shared_ptr<InsetText const> InsetTabular::cell(idx_type idx) const
3849 {
3850         return tabular.getCellInset(idx);
3851 }
3852
3853
3854 shared_ptr<InsetText> InsetTabular::cell(idx_type idx)
3855 {
3856         return tabular.getCellInset(idx);
3857 }
3858
3859
3860 void InsetTabular::cursorPos(BufferView const & bv,
3861                 CursorSlice const & sl, bool boundary, int & x, int & y) const
3862 {
3863         cell(sl.idx())->cursorPos(bv, sl, boundary, x, y);
3864
3865         // y offset     correction
3866         int const row = tabular.cellRow(sl.idx());
3867         for (int i = 0; i <= row; ++i) {
3868                 if (i != 0) {
3869                         y += tabular.rowAscent(i);
3870                         y += tabular.getAdditionalHeight(i);
3871                 }
3872                 if (i != row)
3873                         y += tabular.rowDescent(i);
3874         }
3875
3876         // x offset correction
3877         int const col = tabular.cellColumn(sl.idx());
3878         int idx = tabular.cellIndex(row, 0);
3879         for (int j = 0; j < col; ++j) {
3880                 if (tabular.isPartOfMultiColumn(row, j))
3881                         continue;
3882                 x += tabular.columnWidth(idx);
3883                 ++idx;
3884         }
3885         x += tabular.getBeginningOfTextInCell(idx);
3886         x += ADD_TO_TABULAR_WIDTH;
3887         x += scx_;
3888 }
3889
3890
3891 int InsetTabular::dist(BufferView & bv, idx_type const cell, int x, int y) const
3892 {
3893         int xx = 0;
3894         int yy = 0;
3895         Inset const & inset = *tabular.getCellInset(cell);
3896         Point o = bv.coordCache().getInsets().xy(&inset);
3897         int const xbeg = o.x_ - tabular.getBeginningOfTextInCell(cell);
3898         int const xend = xbeg + tabular.columnWidth(cell);
3899         row_type const row = tabular.cellRow(cell);
3900         int const ybeg = o.y_ - tabular.rowAscent(row) -
3901                          tabular.getAdditionalHeight(row);
3902         int const yend = o.y_ + tabular.rowDescent(row);
3903
3904         if (x < xbeg)
3905                 xx = xbeg - x;
3906         else if (x > xend)
3907                 xx = x - xend;
3908
3909         if (y < ybeg)
3910                 yy = ybeg - y;
3911         else if (y > yend)
3912                 yy = y - yend;
3913
3914         //lyxerr << " xbeg=" << xbeg << "  xend=" << xend
3915         //       << " ybeg=" << ybeg << " yend=" << yend
3916         //       << " xx=" << xx << " yy=" << yy
3917         //       << " dist=" << xx + yy << endl;
3918         return xx + yy;
3919 }
3920
3921
3922 Inset * InsetTabular::editXY(Cursor & cur, int x, int y)
3923 {
3924         //lyxerr << "InsetTabular::editXY: " << this << endl;
3925         cur.selection() = false;
3926         cur.push(*this);
3927         cur.idx() = getNearestCell(cur.bv(), x, y);
3928         resetPos(cur);
3929         return cur.bv().textMetrics(&cell(cur.idx())->text_).editXY(cur, x, y);
3930 }
3931
3932
3933 void InsetTabular::setCursorFromCoordinates(Cursor & cur, int x, int y) const
3934 {
3935         cur.idx() = getNearestCell(cur.bv(), x, y);
3936         cur.bv().textMetrics(&cell(cur.idx())->text_).setCursorFromCoordinates(cur, x, y);
3937 }
3938
3939
3940 InsetTabular::idx_type InsetTabular::getNearestCell(BufferView & bv, int x, int y) const
3941 {
3942         idx_type idx_min = 0;
3943         int dist_min = std::numeric_limits<int>::max();
3944         for (idx_type i = 0, n = nargs(); i != n; ++i) {
3945                 if (bv.coordCache().getInsets().has(tabular.getCellInset(i).get())) {
3946                         int const d = dist(bv, i, x, y);
3947                         if (d < dist_min) {
3948                                 dist_min = d;
3949                                 idx_min = i;
3950                         }
3951                 }
3952         }
3953         return idx_min;
3954 }
3955
3956
3957 int InsetTabular::getCellXPos(idx_type const cell) const
3958 {
3959         idx_type c = cell;
3960
3961         for (; !tabular.isFirstCellInRow(c); --c)
3962                 ;
3963         int lx = 0;
3964         for (; c < cell; ++c)
3965                 lx += tabular.columnWidth(c);
3966
3967         return lx;
3968 }
3969
3970
3971 void InsetTabular::resetPos(Cursor & cur) const
3972 {
3973         BufferView & bv = cur.bv();
3974         int const maxwidth = bv.workWidth();
3975
3976         if (&cur.inset() != this) {
3977                 scx_ = 0;
3978         } else {
3979                 int const X1 = 0;
3980                 int const X2 = maxwidth;
3981                 int const offset = ADD_TO_TABULAR_WIDTH + 2;
3982                 int const x1 = xo(cur.bv()) + getCellXPos(cur.idx()) + offset;
3983                 int const x2 = x1 + tabular.columnWidth(cur.idx());
3984
3985                 if (x1 < X1)
3986                         scx_ = X1 + 20 - x1;
3987                 else if (x2 > X2)
3988                         scx_ = X2 - 20 - x2;
3989                 else
3990                         scx_ = 0;
3991         }
3992
3993         cur.updateFlags(Update::Force | Update::FitCursor);
3994 }
3995
3996
3997 void InsetTabular::moveNextCell(Cursor & cur)
3998 {
3999         if (isRightToLeft(cur)) {
4000                 if (tabular.isFirstCellInRow(cur.idx())) {
4001                         row_type const row = tabular.cellRow(cur.idx());
4002                         if (row == tabular.rowCount() - 1)
4003                                 return;
4004                         cur.idx() = tabular.getCellBelow(tabular.getLastCellInRow(row));
4005                 } else {
4006                         if (cur.idx() == 0)
4007                                 return;
4008                         --cur.idx();
4009                 }
4010         } else {
4011                 if (tabular.isLastCell(cur.idx()))
4012                         return;
4013                 ++cur.idx();
4014         }
4015         cur.pit() = 0;
4016         cur.pos() = 0;
4017         resetPos(cur);
4018 }
4019
4020
4021 void InsetTabular::movePrevCell(Cursor & cur)
4022 {
4023         if (isRightToLeft(cur)) {
4024                 if (tabular.isLastCellInRow(cur.idx())) {
4025                         row_type const row = tabular.cellRow(cur.idx());
4026                         if (row == 0)
4027                                 return;
4028                         cur.idx() = tabular.getFirstCellInRow(row);
4029                         cur.idx() = tabular.getCellAbove(cur.idx());
4030                 } else {
4031                         if (tabular.isLastCell(cur.idx()))
4032                                 return;
4033                         ++cur.idx();
4034                 }
4035         } else {
4036                 if (cur.idx() == 0) // first cell
4037                         return;
4038                 --cur.idx();
4039         }
4040         cur.pit() = cur.lastpit();
4041         cur.pos() = cur.lastpos();
4042
4043         // FIXME: this accesses the position cache before it is initialized
4044         //resetPos(cur);
4045 }
4046
4047
4048 bool InsetTabular::tabularFeatures(Cursor & cur, string const & what)
4049 {
4050         Tabular::Feature action = Tabular::LAST_ACTION;
4051
4052         int i = 0;
4053         for (; tabularFeature[i].action != Tabular::LAST_ACTION; ++i) {
4054                 string const tmp = tabularFeature[i].feature;
4055
4056                 if (tmp == what.substr(0, tmp.length())) {
4057                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
4058                         //tabularFeatures[i].feature.length()))
4059                         action = tabularFeature[i].action;
4060                         break;
4061                 }
4062         }
4063         if (action == Tabular::LAST_ACTION)
4064                 return false;
4065
4066         string const val =
4067                 ltrim(what.substr(tabularFeature[i].feature.length()));
4068         tabularFeatures(cur, action, val);
4069         return true;
4070 }
4071
4072
4073 static void checkLongtableSpecial(Tabular::ltType & ltt,
4074                           string const & special, bool & flag)
4075 {
4076         if (special == "dl_above") {
4077                 ltt.topDL = flag;
4078                 ltt.set = false;
4079         } else if (special == "dl_below") {
4080                 ltt.bottomDL = flag;
4081                 ltt.set = false;
4082         } else if (special == "empty") {
4083                 ltt.empty = flag;
4084                 ltt.set = false;
4085         } else if (flag) {
4086                 ltt.empty = false;
4087                 ltt.set = true;
4088         }
4089 }
4090
4091 bool InsetTabular::oneCellHasRotationState(bool rotated,
4092                 row_type row_start, row_type row_end,
4093                 col_type col_start, col_type col_end) const {
4094
4095         for (row_type i = row_start; i <= row_end; ++i) {
4096                 for (col_type j = col_start; j <= col_end; ++j) {
4097                         if (tabular.getRotateCell(tabular.cellIndex(i, j))
4098                                 == rotated) {
4099                                 return true;
4100                         }
4101                 }
4102         }
4103         return false;
4104 }
4105
4106 void InsetTabular::tabularFeatures(Cursor & cur,
4107         Tabular::Feature feature, string const & value)
4108 {
4109         BufferView & bv = cur.bv();
4110         col_type sel_col_start;
4111         col_type sel_col_end;
4112         row_type sel_row_start;
4113         row_type sel_row_end;
4114         bool setLines = false;
4115         LyXAlignment setAlign = LYX_ALIGN_LEFT;
4116         Tabular::VAlignment setVAlign = Tabular::LYX_VALIGN_TOP;
4117
4118         switch (feature) {
4119
4120         case Tabular::M_ALIGN_LEFT:
4121         case Tabular::ALIGN_LEFT:
4122                 setAlign = LYX_ALIGN_LEFT;
4123                 break;
4124
4125         case Tabular::M_ALIGN_RIGHT:
4126         case Tabular::ALIGN_RIGHT:
4127                 setAlign = LYX_ALIGN_RIGHT;
4128                 break;
4129
4130         case Tabular::M_ALIGN_CENTER:
4131         case Tabular::ALIGN_CENTER:
4132                 setAlign = LYX_ALIGN_CENTER;
4133                 break;
4134
4135         case Tabular::ALIGN_BLOCK:
4136                 setAlign = LYX_ALIGN_BLOCK;
4137                 break;
4138
4139         case Tabular::M_VALIGN_TOP:
4140         case Tabular::VALIGN_TOP:
4141                 setVAlign = Tabular::LYX_VALIGN_TOP;
4142                 break;
4143
4144         case Tabular::M_VALIGN_BOTTOM:
4145         case Tabular::VALIGN_BOTTOM:
4146                 setVAlign = Tabular::LYX_VALIGN_BOTTOM;
4147                 break;
4148
4149         case Tabular::M_VALIGN_MIDDLE:
4150         case Tabular::VALIGN_MIDDLE:
4151                 setVAlign = Tabular::LYX_VALIGN_MIDDLE;
4152                 break;
4153
4154         default:
4155                 break;
4156         }
4157
4158         cur.recordUndoInset(ATOMIC_UNDO);
4159
4160         getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
4161         row_type const row = tabular.cellRow(cur.idx());
4162         col_type const column = tabular.cellColumn(cur.idx());
4163         bool flag = true;
4164         Tabular::ltType ltt;
4165
4166         switch (feature) {
4167
4168         case Tabular::SET_PWIDTH: {
4169                 Length const len(value);
4170                 tabular.setColumnPWidth(cur, cur.idx(), len);
4171                 if (len.zero()
4172                     && tabular.getAlignment(cur.idx(), true) == LYX_ALIGN_BLOCK)
4173                         tabularFeatures(cur, Tabular::ALIGN_CENTER, string());
4174                 break;
4175         }
4176
4177         case Tabular::SET_MPWIDTH:
4178                 tabular.setMColumnPWidth(cur, cur.idx(), Length(value));
4179                 break;
4180
4181         case Tabular::SET_SPECIAL_COLUMN:
4182         case Tabular::SET_SPECIAL_MULTI:
4183                 tabular.setAlignSpecial(cur.idx(), from_utf8(value), feature);
4184                 break;
4185
4186         case Tabular::APPEND_ROW:
4187                 // append the row into the tabular
4188                 tabular.appendRow(bv.buffer().params(), cur.idx());
4189                 break;
4190
4191         case Tabular::APPEND_COLUMN:
4192                 // append the column into the tabular
4193                 tabular.appendColumn(bv.buffer().params(), cur.idx());
4194                 cur.idx() = tabular.cellIndex(row, column);
4195                 break;
4196
4197         case Tabular::DELETE_ROW:
4198                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4199                         tabular.deleteRow(sel_row_start);
4200                 if (sel_row_start >= tabular.rowCount())
4201                         --sel_row_start;
4202                 cur.idx() = tabular.cellIndex(sel_row_start, column);
4203                 cur.pit() = 0;
4204                 cur.pos() = 0;
4205                 cur.selection() = false;
4206                 break;
4207
4208         case Tabular::DELETE_COLUMN:
4209                 for (col_type i = sel_col_start; i <= sel_col_end; ++i)
4210                         tabular.deleteColumn(sel_col_start);
4211                 if (sel_col_start >= tabular.columnCount())
4212                         --sel_col_start;
4213                 cur.idx() = tabular.cellIndex(row, sel_col_start);
4214                 cur.pit() = 0;
4215                 cur.pos() = 0;
4216                 cur.selection() = false;
4217                 break;
4218
4219         case Tabular::COPY_ROW:
4220                 tabular.copyRow(bv.buffer().params(), row);
4221                 break;
4222
4223         case Tabular::COPY_COLUMN:
4224                 tabular.copyColumn(bv.buffer().params(), column);
4225                 cur.idx() = tabular.cellIndex(row, column);
4226                 break;
4227
4228         case Tabular::M_TOGGLE_LINE_TOP:
4229                 flag = false;
4230         case Tabular::TOGGLE_LINE_TOP: {
4231                 bool lineSet = !tabular.topLine(cur.idx(), flag);
4232                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4233                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4234                                 tabular.setTopLine(
4235                                         tabular.cellIndex(i, j),
4236                                         lineSet, flag);
4237                 break;
4238         }
4239
4240         case Tabular::M_TOGGLE_LINE_BOTTOM:
4241                 flag = false;
4242         case Tabular::TOGGLE_LINE_BOTTOM: {
4243                 bool lineSet = !tabular.bottomLine(cur.idx(), flag);
4244                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4245                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4246                                 tabular.setBottomLine(
4247                                         tabular.cellIndex(i, j),
4248                                         lineSet,
4249                                         flag);
4250                 break;
4251         }
4252
4253         case Tabular::M_TOGGLE_LINE_LEFT:
4254                 flag = false;
4255         case Tabular::TOGGLE_LINE_LEFT: {
4256                 bool lineSet = !tabular.leftLine(cur.idx(), flag);
4257                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4258                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4259                                 tabular.setLeftLine(
4260                                         tabular.cellIndex(i,j),
4261                                         lineSet,
4262                                         flag);
4263                 break;
4264         }
4265
4266         case Tabular::M_TOGGLE_LINE_RIGHT:
4267                 flag = false;
4268         case Tabular::TOGGLE_LINE_RIGHT: {
4269                 bool lineSet = !tabular.rightLine(cur.idx(), flag);
4270                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4271                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4272                                 tabular.setRightLine(
4273                                         tabular.cellIndex(i,j),
4274                                         lineSet,
4275                                         flag);
4276                 break;
4277         }
4278
4279         case Tabular::M_ALIGN_LEFT:
4280         case Tabular::M_ALIGN_RIGHT:
4281         case Tabular::M_ALIGN_CENTER:
4282                 flag = false;
4283         case Tabular::ALIGN_LEFT:
4284         case Tabular::ALIGN_RIGHT:
4285         case Tabular::ALIGN_CENTER:
4286         case Tabular::ALIGN_BLOCK:
4287                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4288                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4289                                 tabular.setAlignment(
4290                                         tabular.cellIndex(i, j),
4291                                         setAlign,
4292                                         flag);
4293                 break;
4294
4295         case Tabular::M_VALIGN_TOP:
4296         case Tabular::M_VALIGN_BOTTOM:
4297         case Tabular::M_VALIGN_MIDDLE:
4298                 flag = false;
4299         case Tabular::VALIGN_TOP:
4300         case Tabular::VALIGN_BOTTOM:
4301         case Tabular::VALIGN_MIDDLE:
4302                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4303                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4304                                 tabular.setVAlignment(
4305                                         tabular.cellIndex(i, j),
4306                                         setVAlign, flag);
4307                 break;
4308
4309         case Tabular::MULTICOLUMN: {
4310                 if (sel_row_start != sel_row_end) {
4311                         // FIXME: Need I say it ? This is horrible.
4312                         // FIXME UNICODE
4313                         Alert::error(_("Error setting multicolumn"),
4314                                      _("You cannot set multicolumn vertically."));
4315                         return;
4316                 }
4317                 if (!cur.selection()) {
4318                         // just multicol for one single cell
4319                         // check whether we are completely in a multicol
4320                         if (tabular.isMultiColumn(cur.idx()))
4321                                 tabular.unsetMultiColumn(cur.idx());
4322                         else
4323                                 tabular.setMultiColumn(&bv.buffer(), cur.idx(), 1);
4324                         break;
4325                 }
4326                 // we have a selection so this means we just add all this
4327                 // cells to form a multicolumn cell
4328                 idx_type const s_start = cur.selBegin().idx();
4329                 idx_type const s_end = cur.selEnd().idx();
4330                 tabular.setMultiColumn(&bv.buffer(), s_start, s_end - s_start + 1);
4331                 cur.idx() = s_start;
4332                 cur.pit() = 0;
4333                 cur.pos() = 0;
4334                 cur.selection() = false;
4335                 break;
4336         }
4337
4338         case Tabular::SET_ALL_LINES:
4339                 setLines = true;
4340         case Tabular::UNSET_ALL_LINES:
4341                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4342                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4343                                 tabular.setAllLines(
4344                                         tabular.cellIndex(i,j), setLines);
4345                 break;
4346
4347         case Tabular::SET_LONGTABULAR:
4348                 tabular.setLongTabular(true);
4349                 break;
4350
4351         case Tabular::UNSET_LONGTABULAR:
4352                 tabular.setLongTabular(false);
4353                 break;
4354
4355         case Tabular::SET_ROTATE_TABULAR:
4356                 tabular.setRotateTabular(true);
4357                 break;
4358
4359         case Tabular::UNSET_ROTATE_TABULAR:
4360                 tabular.setRotateTabular(false);
4361                 break;
4362
4363         case Tabular::TOGGLE_ROTATE_TABULAR:
4364                 tabular.setRotateTabular(!tabular.getRotateTabular());
4365                 break;
4366
4367         case Tabular::SET_ROTATE_CELL:
4368                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4369                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4370                                 tabular.setRotateCell(
4371                                         tabular.cellIndex(i, j), true);
4372                 break;
4373
4374         case Tabular::UNSET_ROTATE_CELL:
4375                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4376                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4377                                 tabular.setRotateCell(
4378                                         tabular.cellIndex(i, j), false);
4379                 break;
4380
4381         case Tabular::TOGGLE_ROTATE_CELL:
4382                 {
4383                 bool oneNotRotated = oneCellHasRotationState(false,
4384                         sel_row_start, sel_row_end, sel_col_start, sel_col_end);
4385
4386                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4387                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4388                                 tabular.setRotateCell(tabular.cellIndex(i, j),
4389                                                                           oneNotRotated);
4390                 }
4391                 break;
4392
4393         case Tabular::SET_USEBOX: {
4394                 Tabular::BoxType val = Tabular::BoxType(convert<int>(value));
4395                 if (val == tabular.getUsebox(cur.idx()))
4396                         val = Tabular::BOX_NONE;
4397                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4398                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
4399                                 tabular.setUsebox(tabular.cellIndex(i, j), val);
4400                 break;
4401         }
4402
4403         case Tabular::UNSET_LTFIRSTHEAD:
4404                 flag = false;
4405         case Tabular::SET_LTFIRSTHEAD:
4406                 tabular.getRowOfLTFirstHead(row, ltt);
4407                 checkLongtableSpecial(ltt, value, flag);
4408                 tabular.setLTHead(row, flag, ltt, true);
4409                 break;
4410
4411         case Tabular::UNSET_LTHEAD:
4412                 flag = false;
4413         case Tabular::SET_LTHEAD:
4414                 tabular.getRowOfLTHead(row, ltt);
4415                 checkLongtableSpecial(ltt, value, flag);
4416                 tabular.setLTHead(row, flag, ltt, false);
4417                 break;
4418
4419         case Tabular::UNSET_LTFOOT:
4420                 flag = false;
4421         case Tabular::SET_LTFOOT:
4422                 tabular.getRowOfLTFoot(row, ltt);
4423                 checkLongtableSpecial(ltt, value, flag);
4424                 tabular.setLTFoot(row, flag, ltt, false);
4425                 break;
4426
4427         case Tabular::UNSET_LTLASTFOOT:
4428                 flag = false;
4429         case Tabular::SET_LTLASTFOOT:
4430                 tabular.getRowOfLTLastFoot(row, ltt);
4431                 checkLongtableSpecial(ltt, value, flag);
4432                 tabular.setLTFoot(row, flag, ltt, true);
4433                 break;
4434
4435         case Tabular::SET_LTNEWPAGE:
4436                 tabular.setLTNewPage(row, !tabular.getLTNewPage(row));
4437                 break;
4438
4439         case Tabular::SET_BOOKTABS:
4440                 tabular.setBookTabs(true);
4441                 break;
4442
4443         case Tabular::UNSET_BOOKTABS:
4444                 tabular.setBookTabs(false);
4445                 break;
4446
4447         case Tabular::SET_TOP_SPACE: {
4448                 Length len;
4449                 if (value == "default")
4450                         for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4451                                 tabular.row_info[i].top_space_default = true;
4452                 else if (isValidLength(value, &len))
4453                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
4454                                 tabular.row_info[i].top_space_default = false;
4455                                 tabular.row_info[i].top_space = len;
4456                         }
4457                 else
4458                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
4459                                 tabular.row_info[i].top_space_default = false;
4460                                 tabular.row_info[i].top_space = len;
4461                         }
4462                 break;
4463         }
4464
4465         case Tabular::SET_BOTTOM_SPACE: {
4466                 Length len;
4467                 if (value == "default")
4468                         for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4469                                 tabular.row_info[i].bottom_space_default = true;
4470                 else if (isValidLength(value, &len))
4471                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
4472                                 tabular.row_info[i].bottom_space_default = false;
4473                                 tabular.row_info[i].bottom_space = len;
4474                         }
4475                 else
4476                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
4477                                 tabular.row_info[i].bottom_space_default = false;
4478                                 tabular.row_info[i].bottom_space = len;
4479                         }
4480                 break;
4481         }
4482
4483         case Tabular::SET_INTERLINE_SPACE: {
4484                 Length len;
4485                 if (value == "default")
4486                         for (row_type i = sel_row_start; i <= sel_row_end; ++i)
4487                                 tabular.row_info[i].interline_space_default = true;
4488                 else if (isValidLength(value, &len))
4489                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
4490                                 tabular.row_info[i].interline_space_default = false;
4491                                 tabular.row_info[i].interline_space = len;
4492                         }
4493                 else
4494                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
4495                                 tabular.row_info[i].interline_space_default = false;
4496                                 tabular.row_info[i].interline_space = len;
4497                         }
4498                 break;
4499         }
4500
4501         // dummy stuff just to avoid warnings
4502         case Tabular::LAST_ACTION:
4503                 break;
4504         }
4505 }
4506
4507
4508 bool InsetTabular::showInsetDialog(BufferView * bv) const
4509 {
4510         InsetTabularMailer(*this).showDialog(bv);
4511         return true;
4512 }
4513
4514
4515 void InsetTabular::openLayoutDialog(BufferView * bv) const
4516 {
4517         InsetTabularMailer(*this).showDialog(bv);
4518 }
4519
4520
4521 bool InsetTabular::copySelection(Cursor & cur)
4522 {
4523         if (!cur.selection())
4524                 return false;
4525
4526         row_type rs, re;
4527         col_type cs, ce;
4528         getSelection(cur, rs, re, cs, ce);
4529
4530         paste_tabular.reset(new Tabular(tabular));
4531
4532         for (row_type i = 0; i < rs; ++i)
4533                 paste_tabular->deleteRow(0);
4534
4535         row_type const rows = re - rs + 1;
4536         while (paste_tabular->rowCount() > rows)
4537                 paste_tabular->deleteRow(rows);
4538
4539         paste_tabular->setTopLine(0, true, true);
4540         paste_tabular->setBottomLine(paste_tabular->getFirstCellInRow(rows - 1),
4541                                      true, true);
4542
4543         for (col_type i = 0; i < cs; ++i)
4544                 paste_tabular->deleteColumn(0);
4545
4546         col_type const columns = ce - cs + 1;
4547         while (paste_tabular->columnCount() > columns)
4548                 paste_tabular->deleteColumn(columns);
4549
4550         paste_tabular->setLeftLine(0, true, true);
4551         paste_tabular->setRightLine(paste_tabular->getLastCellInRow(0),
4552                                     true, true);
4553
4554         odocstringstream os;
4555         OutputParams const runparams(0);
4556         paste_tabular->plaintext(cur.buffer(), os, runparams, 0, true, '\t');
4557         // Needed for the "Edit->Paste recent" menu and the system clipboard.
4558         cap::copySelection(cur, os.str());
4559
4560         // mark tabular stack dirty
4561         // FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
4562         // when we (hopefully) have a one-for-all paste mechanism.
4563         // This must be called after cap::copySelection.
4564         dirtyTabularStack(true);
4565
4566         return true;
4567 }
4568
4569
4570 bool InsetTabular::pasteClipboard(Cursor & cur)
4571 {
4572         if (!paste_tabular)
4573                 return false;
4574         col_type const actcol = tabular.cellColumn(cur.idx());
4575         row_type const actrow = tabular.cellRow(cur.idx());
4576         for (row_type r1 = 0, r2 = actrow;
4577              r1 < paste_tabular->rowCount() && r2 < tabular.rowCount();
4578              ++r1, ++r2) {
4579                 for (col_type c1 = 0, c2 = actcol;
4580                     c1 < paste_tabular->columnCount() && c2 < tabular.columnCount();
4581                     ++c1, ++c2) {
4582                         if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
4583                             tabular.isPartOfMultiColumn(r2, c2))
4584                                 continue;
4585                         if (paste_tabular->isPartOfMultiColumn(r1, c1)) {
4586                                 --c2;
4587                                 continue;
4588                         }
4589                         if (tabular.isPartOfMultiColumn(r2, c2)) {
4590                                 --c1;
4591                                 continue;
4592                         }
4593                         shared_ptr<InsetText> inset(
4594                                 new InsetText(*paste_tabular->getCellInset(r1, c1)));
4595                         tabular.setCellInset(r2, c2, inset);
4596                         // FIXME: change tracking (MG)
4597                         inset->setChange(Change(cur.buffer().params().trackChanges ?
4598                                                 Change::INSERTED : Change::UNCHANGED));
4599                         cur.pos() = 0;
4600                 }
4601         }
4602         return true;
4603 }
4604
4605
4606 void InsetTabular::cutSelection(Cursor & cur)
4607 {
4608         if (!cur.selection())
4609                 return;
4610
4611         row_type rs, re;
4612         col_type cs, ce;
4613         getSelection(cur, rs, re, cs, ce);
4614         for (row_type i = rs; i <= re; ++i) {
4615                 for (col_type j = cs; j <= ce; ++j) {
4616                         shared_ptr<InsetText> t
4617                                 = cell(tabular.cellIndex(i, j));
4618                         if (cur.buffer().params().trackChanges)
4619                                 // FIXME: Change tracking (MG)
4620                                 t->setChange(Change(Change::DELETED));
4621                         else
4622                                 t->clear();
4623                 }
4624         }
4625
4626         // cursor position might be invalid now
4627         if (cur.pit() > cur.lastpit())
4628                 cur.pit() = cur.lastpit();
4629         if (cur.pos() > cur.lastpos())
4630                 cur.pos() = cur.lastpos();
4631         cur.clearSelection();
4632 }
4633
4634
4635 bool InsetTabular::isRightToLeft(Cursor & cur) const
4636 {
4637         BOOST_ASSERT(cur.depth() > 1);
4638         Paragraph const & parentpar = cur[cur.depth() - 2].paragraph();
4639         pos_type const parentpos = cur[cur.depth() - 2].pos();
4640         return parentpar.getFontSettings(cur.bv().buffer().params(),
4641                                          parentpos).language()->rightToLeft();
4642 }
4643
4644
4645 void InsetTabular::getSelection(Cursor & cur,
4646         row_type & rs, row_type & re, col_type & cs, col_type & ce) const
4647 {
4648         CursorSlice const & beg = cur.selBegin();
4649         CursorSlice const & end = cur.selEnd();
4650         cs = tabular.cellColumn(beg.idx());
4651         ce = tabular.cellColumn(end.idx());
4652         if (cs > ce) {
4653                 ce = cs;
4654                 cs = tabular.cellColumn(end.idx());
4655         } else {
4656                 ce = tabular.cellRightColumn(end.idx());
4657         }
4658
4659         rs = tabular.cellRow(beg.idx());
4660         re = tabular.cellRow(end.idx());
4661         if (rs > re)
4662                 swap(rs, re);
4663 }
4664
4665
4666 Text * InsetTabular::getText(int idx) const
4667 {
4668         return size_t(idx) < nargs() ? cell(idx)->getText(0) : 0;
4669 }
4670
4671
4672 void InsetTabular::setChange(Change const & change)
4673 {
4674         for (idx_type idx = 0; idx < nargs(); ++idx)
4675                 cell(idx)->setChange(change);
4676 }
4677
4678
4679 void InsetTabular::acceptChanges(BufferParams const & bparams)
4680 {
4681         for (idx_type idx = 0; idx < nargs(); ++idx)
4682                 cell(idx)->acceptChanges(bparams);
4683 }
4684
4685
4686 void InsetTabular::rejectChanges(BufferParams const & bparams)
4687 {
4688         for (idx_type idx = 0; idx < nargs(); ++idx)
4689                 cell(idx)->rejectChanges(bparams);
4690 }
4691
4692
4693 bool InsetTabular::forceDefaultParagraphs(idx_type cell) const
4694 {
4695         return tabular.getPWidth(cell).zero();
4696 }
4697
4698
4699 bool InsetTabular::insertPlaintextString(BufferView & bv, docstring const & buf,
4700                                      bool usePaste)
4701 {
4702         if (buf.length() <= 0)
4703                 return true;
4704
4705         Buffer const & buffer = bv.buffer();
4706
4707         col_type cols = 1;
4708         row_type rows = 1;
4709         col_type maxCols = 1;
4710         docstring::size_type const len = buf.length();
4711         docstring::size_type p = 0;
4712
4713         while (p < len &&
4714                (p = buf.find_first_of(from_ascii("\t\n"), p)) != docstring::npos) {
4715                 switch (buf[p]) {
4716                 case '\t':
4717                         ++cols;
4718                         break;
4719                 case '\n':
4720                         if (p + 1 < len)
4721                                 ++rows;
4722                         maxCols = max(cols, maxCols);
4723                         cols = 1;
4724                         break;
4725                 }
4726                 ++p;
4727         }
4728         maxCols = max(cols, maxCols);
4729         Tabular * loctab;
4730         idx_type cell = 0;
4731         col_type ocol = 0;
4732         row_type row = 0;
4733         if (usePaste) {
4734                 paste_tabular.reset(
4735                         new Tabular(buffer.params(), rows, maxCols));
4736                 loctab = paste_tabular.get();
4737                 cols = 0;
4738                 dirtyTabularStack(true);
4739         } else {
4740                 loctab = &tabular;
4741                 cell = bv.cursor().idx();
4742                 ocol = tabular.cellColumn(cell);
4743                 row = tabular.cellRow(cell);
4744         }
4745
4746         docstring::size_type op = 0;
4747         idx_type const cells = loctab->cellCount();
4748         p = 0;
4749         cols = ocol;
4750         rows = loctab->rowCount();
4751         col_type const columns = loctab->columnCount();
4752
4753         while (cell < cells && p < len && row < rows &&
4754                (p = buf.find_first_of(from_ascii("\t\n"), p)) != docstring::npos)
4755         {
4756                 if (p >= len)
4757                         break;
4758                 switch (buf[p]) {
4759                 case '\t':
4760                         // we can only set this if we are not too far right
4761                         if (cols < columns) {
4762                                 shared_ptr<InsetText> inset = loctab->getCellInset(cell);
4763                                 Font const font = bv.textMetrics(&inset->text_).
4764                                         getDisplayFont(0, 0);
4765                                 inset->setText(buf.substr(op, p - op), font,
4766                                                buffer.params().trackChanges);
4767                                 ++cols;
4768                                 ++cell;
4769                         }
4770                         break;
4771                 case '\n':
4772                         // we can only set this if we are not too far right
4773                         if (cols < columns) {
4774                                 shared_ptr<InsetText> inset = tabular.getCellInset(cell);
4775                                 Font const font = bv.textMetrics(&inset->text_).
4776                                         getDisplayFont(0, 0);
4777                                 inset->setText(buf.substr(op, p - op), font,
4778                                                buffer.params().trackChanges);
4779                         }
4780                         cols = ocol;
4781                         ++row;
4782                         if (row < rows)
4783                                 cell = loctab->cellIndex(row, cols);
4784                         break;
4785                 }
4786                 ++p;
4787                 op = p;
4788         }
4789         // check for the last cell if there is no trailing '\n'
4790         if (cell < cells && op < len) {
4791                 shared_ptr<InsetText> inset = loctab->getCellInset(cell);
4792                 Font const font = bv.textMetrics(&inset->text_).getDisplayFont(0, 0);
4793                 inset->setText(buf.substr(op, len - op), font,
4794                         buffer.params().trackChanges);
4795         }
4796         return true;
4797 }
4798
4799
4800 void InsetTabular::addPreview(PreviewLoader & loader) const
4801 {
4802         row_type const rows = tabular.rowCount();
4803         col_type const columns = tabular.columnCount();
4804         for (row_type i = 0; i < rows; ++i) {
4805                 for (col_type j = 0; j < columns; ++j)
4806                         tabular.getCellInset(i, j)->addPreview(loader);
4807         }
4808 }
4809
4810
4811 bool InsetTabular::tablemode(Cursor & cur) const
4812 {
4813         return cur.selection() && cur.selBegin().idx() != cur.selEnd().idx();
4814 }
4815
4816
4817
4818
4819 string const InsetTabularMailer::name_("tabular");
4820
4821 InsetTabularMailer::InsetTabularMailer(InsetTabular const & inset)
4822         : inset_(const_cast<InsetTabular &>(inset))
4823 {}
4824
4825
4826 string const InsetTabularMailer::inset2string(Buffer const &) const
4827 {
4828         return params2string(inset_);
4829 }
4830
4831
4832 void InsetTabularMailer::string2params(string const & in, InsetTabular & inset)
4833 {
4834         istringstream data(in);
4835         Lexer lex(0,0);
4836         lex.setStream(data);
4837
4838         if (in.empty())
4839                 return;
4840
4841         string token;
4842         lex >> token;
4843         if (!lex || token != name_)
4844                 return print_mailer_error("InsetTabularMailer", in, 1,
4845                                           name_);
4846
4847         // This is part of the inset proper that is usually swallowed
4848         // by Buffer::readInset
4849         lex >> token;
4850         if (!lex || token != "Tabular")
4851                 return print_mailer_error("InsetTabularMailer", in, 2,
4852                                           "Tabular");
4853
4854         Buffer const & buffer = inset.buffer();
4855         inset.read(buffer, lex);
4856 }
4857
4858
4859 string const InsetTabularMailer::params2string(InsetTabular const & inset)
4860 {
4861         ostringstream data;
4862         data << name_ << ' ';
4863         inset.write(inset.buffer(), data);
4864         data << "\\end_inset\n";
4865         return data.str();
4866 }
4867
4868
4869 } // namespace lyx