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