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