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