]> git.lyx.org Git - lyx.git/blob - src/insets/insettabular.C
First part of r14315 from the younes branch:
[lyx.git] / src / insets / insettabular.C
1 /**
2  * \file insettabular.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "insettabular.h"
14
15 #include "buffer.h"
16 #include "bufferparams.h"
17 #include "BufferView.h"
18 #include "cursor.h"
19 #include "CutAndPaste.h"
20 #include "coordcache.h"
21 #include "debug.h"
22 #include "dispatchresult.h"
23 #include "funcrequest.h"
24 #include "FuncStatus.h"
25 #include "gettext.h"
26 #include "language.h"
27 #include "LColor.h"
28 #include "lyx_cb.h"
29 #include "lyxlex.h"
30 #include "metricsinfo.h"
31 #include "outputparams.h"
32 #include "paragraph.h"
33 #include "paragraph_funcs.h"
34 #include "ParagraphParameters.h"
35 #include "undo.h"
36
37 #include "support/convert.h"
38
39 #include "frontends/Alert.h"
40 #include "frontends/font_metrics.h"
41 #include "frontends/Gui.h"
42 #include "frontends/LyXView.h"
43 #include "frontends/Clipboard.h"
44 #include "frontends/Painter.h"
45 #include "frontends/nullpainter.h"
46
47 #include <sstream>
48 #include <iostream>
49 #include <limits>
50
51 using lyx::cap::dirtyTabularStack;
52 using lyx::cap::tabularStackDirty;
53
54 using lyx::graphics::PreviewLoader;
55
56 using lyx::support::ltrim;
57
58 using lyx::frontend::Painter;
59 using lyx::frontend::Gui;
60 using lyx::frontend::Clipboard;
61
62 using boost::shared_ptr;
63
64 using std::auto_ptr;
65 using std::endl;
66 using std::max;
67 using std::string;
68 using std::istringstream;
69 using std::ostream;
70 using std::ostringstream;
71 using std::swap;
72 using std::vector;
73
74
75 namespace {
76
77 int const ADD_TO_HEIGHT = 2;
78 int const ADD_TO_TABULAR_WIDTH = 2;
79 int const default_line_space = 10;
80
81 ///
82 boost::scoped_ptr<LyXTabular> paste_tabular;
83
84
85 struct TabularFeature {
86         LyXTabular::Feature action;
87         string feature;
88 };
89
90
91 TabularFeature tabularFeature[] =
92 {
93         { LyXTabular::APPEND_ROW, "append-row" },
94         { LyXTabular::APPEND_COLUMN, "append-column" },
95         { LyXTabular::DELETE_ROW, "delete-row" },
96         { LyXTabular::DELETE_COLUMN, "delete-column" },
97         { LyXTabular::TOGGLE_LINE_TOP, "toggle-line-top" },
98         { LyXTabular::TOGGLE_LINE_BOTTOM, "toggle-line-bottom" },
99         { LyXTabular::TOGGLE_LINE_LEFT, "toggle-line-left" },
100         { LyXTabular::TOGGLE_LINE_RIGHT, "toggle-line-right" },
101         { LyXTabular::ALIGN_LEFT, "align-left" },
102         { LyXTabular::ALIGN_RIGHT, "align-right" },
103         { LyXTabular::ALIGN_CENTER, "align-center" },
104         { LyXTabular::ALIGN_BLOCK, "align-block" },
105         { LyXTabular::VALIGN_TOP, "valign-top" },
106         { LyXTabular::VALIGN_BOTTOM, "valign-bottom" },
107         { LyXTabular::VALIGN_MIDDLE, "valign-middle" },
108         { LyXTabular::M_TOGGLE_LINE_TOP, "m-toggle-line-top" },
109         { LyXTabular::M_TOGGLE_LINE_BOTTOM, "m-toggle-line-bottom" },
110         { LyXTabular::M_TOGGLE_LINE_LEFT, "m-toggle-line-left" },
111         { LyXTabular::M_TOGGLE_LINE_RIGHT, "m-toggle-line-right" },
112         { LyXTabular::M_ALIGN_LEFT, "m-align-left" },
113         { LyXTabular::M_ALIGN_RIGHT, "m-align-right" },
114         { LyXTabular::M_ALIGN_CENTER, "m-align-center" },
115         { LyXTabular::M_VALIGN_TOP, "m-valign-top" },
116         { LyXTabular::M_VALIGN_BOTTOM, "m-valign-bottom" },
117         { LyXTabular::M_VALIGN_MIDDLE, "m-valign-middle" },
118         { LyXTabular::MULTICOLUMN, "multicolumn" },
119         { LyXTabular::SET_ALL_LINES, "set-all-lines" },
120         { LyXTabular::UNSET_ALL_LINES, "unset-all-lines" },
121         { LyXTabular::SET_LONGTABULAR, "set-longtabular" },
122         { LyXTabular::UNSET_LONGTABULAR, "unset-longtabular" },
123         { LyXTabular::SET_PWIDTH, "set-pwidth" },
124         { LyXTabular::SET_MPWIDTH, "set-mpwidth" },
125         { LyXTabular::SET_ROTATE_TABULAR, "set-rotate-tabular" },
126         { LyXTabular::UNSET_ROTATE_TABULAR, "unset-rotate-tabular" },
127         { LyXTabular::SET_ROTATE_CELL, "set-rotate-cell" },
128         { LyXTabular::UNSET_ROTATE_CELL, "unset-rotate-cell" },
129         { LyXTabular::SET_USEBOX, "set-usebox" },
130         { LyXTabular::SET_LTHEAD, "set-lthead" },
131         { LyXTabular::UNSET_LTHEAD, "unset-lthead" },
132         { LyXTabular::SET_LTFIRSTHEAD, "set-ltfirsthead" },
133         { LyXTabular::UNSET_LTFIRSTHEAD, "unset-ltfirsthead" },
134         { LyXTabular::SET_LTFOOT, "set-ltfoot" },
135         { LyXTabular::UNSET_LTFOOT, "unset-ltfoot" },
136         { LyXTabular::SET_LTLASTFOOT, "set-ltlastfoot" },
137         { LyXTabular::UNSET_LTLASTFOOT, "unset-ltlastfoot" },
138         { LyXTabular::SET_LTNEWPAGE, "set-ltnewpage" },
139         { LyXTabular::SET_SPECIAL_COLUMN, "set-special-column" },
140         { LyXTabular::SET_SPECIAL_MULTI, "set-special-multi" },
141         { LyXTabular::SET_BOOKTABS, "set-booktabs" },
142         { LyXTabular::UNSET_BOOKTABS, "unset-booktabs" },
143         { LyXTabular::SET_TOP_SPACE, "set-top-space" },
144         { LyXTabular::SET_BOTTOM_SPACE, "set-bottom-space" },
145         { LyXTabular::SET_INTERLINE_SPACE, "set-interline-space" },
146         { LyXTabular::LAST_ACTION, "" }
147 };
148
149
150 class FeatureEqual : public std::unary_function<TabularFeature, bool> {
151 public:
152         FeatureEqual(LyXTabular::Feature feature)
153                 : feature_(feature) {}
154         bool operator()(TabularFeature const & tf) const {
155                 return tf.action == feature_;
156         }
157 private:
158         LyXTabular::Feature feature_;
159 };
160
161 } // namespace anon
162
163
164 string const featureAsString(LyXTabular::Feature feature)
165 {
166         TabularFeature * end = tabularFeature +
167                 sizeof(tabularFeature) / sizeof(TabularFeature);
168         TabularFeature * it = std::find_if(tabularFeature, end,
169                                            FeatureEqual(feature));
170         return (it == end) ? string() : it->feature;
171 }
172
173
174 bool InsetTabular::hasPasteBuffer() const
175 {
176         return (paste_tabular.get() != 0);
177 }
178
179
180 InsetTabular::InsetTabular(Buffer const & buf, row_type rows,
181                            col_type columns)
182         : tabular(buf.params(), max(rows, row_type(1)),
183           max(columns, col_type(1))), buffer_(&buf), scx_(0)
184 {}
185
186
187 InsetTabular::InsetTabular(InsetTabular const & tab)
188         : InsetOld(tab), tabular(tab.tabular),
189                 buffer_(tab.buffer_), scx_(0)
190 {}
191
192
193 InsetTabular::~InsetTabular()
194 {
195         InsetTabularMailer(*this).hideDialog();
196 }
197
198
199 auto_ptr<InsetBase> InsetTabular::doClone() const
200 {
201         return auto_ptr<InsetBase>(new InsetTabular(*this));
202 }
203
204
205 Buffer const & InsetTabular::buffer() const
206 {
207         return *buffer_;
208 }
209
210
211 void InsetTabular::buffer(Buffer const * b)
212 {
213         buffer_ = b;
214 }
215
216
217 void InsetTabular::write(Buffer const & buf, ostream & os) const
218 {
219         os << "Tabular" << endl;
220         tabular.write(buf, os);
221 }
222
223
224 void InsetTabular::read(Buffer const & buf, LyXLex & lex)
225 {
226         bool const old_format = (lex.getString() == "\\LyXTable");
227
228         tabular.read(buf, lex);
229
230         if (old_format)
231                 return;
232
233         lex.next();
234         string token = lex.getString();
235         while (lex.isOK() && (token != "\\end_inset")) {
236                 lex.next();
237                 token = lex.getString();
238         }
239         if (token != "\\end_inset") {
240                 lex.printError("Missing \\end_inset at this point. "
241                                "Read: `$$Token'");
242         }
243 }
244
245
246 void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
247 {
248         //lyxerr << "InsetTabular::metrics: " << mi.base.bv << " width: " <<
249         //      mi.base.textwidth << "\n";
250         if (!mi.base.bv) {
251                 lyxerr << "InsetTabular::metrics: need bv" << endl;
252                 BOOST_ASSERT(false);
253         }
254
255         row_type i = 0;
256         for (idx_type cell = 0; i < tabular.rows(); ++i) {
257                 int maxAsc = 0;
258                 int maxDesc = 0;
259                 for (col_type j = 0; j < tabular.columns(); ++j) {
260                         if (tabular.isPartOfMultiColumn(i, j))
261                                 // Multicolumn cell, but not first one
262                                 continue;
263                         Dimension dim;
264                         MetricsInfo m = mi;
265                         LyXLength p_width;
266                         if (tabular.cell_info[i][j].multicolumn ==
267                             LyXTabular::CELL_BEGIN_OF_MULTICOLUMN)
268                                 p_width = tabular.cellinfo_of_cell(cell).p_width;
269                         else
270                                 p_width = tabular.column_info[j].p_width;
271                         if (!p_width.zero())
272                                 m.base.textwidth = p_width.inPixels(mi.base.textwidth);
273                         tabular.getCellInset(cell)->metrics(m, dim);
274                         if (!p_width.zero())
275                                 dim.wid = m.base.textwidth;
276                         maxAsc  = max(maxAsc, dim.asc);
277                         maxDesc = max(maxDesc, dim.des);
278                         tabular.setWidthOfCell(cell, dim.wid);
279                         ++cell;
280                 }
281                 int const top_space = tabular.row_info[i].top_space_default ?
282                         default_line_space :
283                         tabular.row_info[i].top_space.inPixels(mi.base.textwidth);
284                 tabular.setAscentOfRow(i, maxAsc + ADD_TO_HEIGHT + top_space);
285                 int const bottom_space = tabular.row_info[i].bottom_space_default ?
286                         default_line_space :
287                         tabular.row_info[i].bottom_space.inPixels(mi.base.textwidth);
288                 tabular.setDescentOfRow(i, maxDesc + ADD_TO_HEIGHT + bottom_space);
289         }
290
291         dim.asc = tabular.getAscentOfRow(0);
292         dim.des = tabular.getHeightOfTabular() - dim.asc;
293         dim.wid = tabular.getWidthOfTabular() + 2 * ADD_TO_TABULAR_WIDTH;
294         dim_ = dim;
295 }
296
297
298 void InsetTabular::draw(PainterInfo & pi, int x, int y) const
299 {
300         setPosCache(pi, x, y);
301
302         //lyxerr << "InsetTabular::draw: " << x << " " << y << endl;
303         BufferView * bv = pi.base.bv;
304
305         static lyx::frontend::NullPainter nop;
306         static PainterInfo nullpi(bv, nop);
307
308         //resetPos(bv->cursor());
309
310         x += scx_;
311         x += ADD_TO_TABULAR_WIDTH;
312
313         idx_type idx = 0;
314         first_visible_cell = LyXTabular::npos;
315         for (row_type i = 0; i < tabular.rows(); ++i) {
316                 int nx = x;
317                 int const a = tabular.getAscentOfRow(i);
318                 int const d = tabular.getDescentOfRow(i);
319                 idx = tabular.getCellNumber(i, 0);
320                 for (col_type j = 0; j < tabular.columns(); ++j) {
321                         if (tabular.isPartOfMultiColumn(i, j))
322                                 continue;
323                         if (first_visible_cell == LyXTabular::npos)
324                                 first_visible_cell = idx;
325
326                         int const cx = nx + tabular.getBeginningOfTextInCell(idx);
327                         if (nx + tabular.getWidthOfColumn(idx) < 0
328                             || nx > bv->workWidth()
329                             || y + d < 0
330                             || y - a > bv->workHeight()) {
331                                 cell(idx)->draw(nullpi, cx, y);
332                                 drawCellLines(nop, nx, y, i, idx, pi.erased_);
333                         } else {
334                                 cell(idx)->draw(pi, cx, y);
335                                 drawCellLines(pi.pain, nx, y, i, idx, pi.erased_);
336                         }
337                         nx += tabular.getWidthOfColumn(idx);
338                         ++idx;
339                 }
340
341                 if (i + 1 < tabular.rows())
342                         y += d + tabular.getAscentOfRow(i + 1) +
343                                 tabular.getAdditionalHeight(i + 1);
344         }
345 }
346
347
348 void InsetTabular::drawSelection(PainterInfo & pi, int x, int y) const
349 {
350         setPosCache(pi, x, y);
351
352         LCursor & cur = pi.base.bv->cursor();
353
354         x += scx_ + ADD_TO_TABULAR_WIDTH;
355
356         // Paint background of current tabular
357         int const w = tabular.getWidthOfTabular();
358         int const h = tabular.getHeightOfTabular();
359         int yy = y - tabular.getAscentOfRow(0);
360         pi.pain.fillRectangle(x, yy, w, h, backgroundColor());
361
362         if (!cur.selection())
363                 return;
364         if (!ptr_cmp(&cur.inset(), this))
365                 return;
366
367         //resetPos(cur);
368
369
370         if (tablemode(cur)) {
371                 row_type rs, re;
372                 col_type cs, ce;
373                 getSelection(cur, rs, re, cs, ce);
374                 y -= tabular.getAscentOfRow(0);
375                 for (row_type j = 0; j < tabular.rows(); ++j) {
376                         int const a = tabular.getAscentOfRow(j);
377                         int const h = a + tabular.getDescentOfRow(j);
378                         int xx = x;
379                         y += tabular.getAdditionalHeight(j);
380                         for (col_type i = 0; i < tabular.columns(); ++i) {
381                                 if (tabular.isPartOfMultiColumn(j, i))
382                                         continue;
383                                 idx_type const cell =
384                                         tabular.getCellNumber(j, i);
385                                 int const w = tabular.getWidthOfColumn(cell);
386                                 if (i >= cs && i <= ce && j >= rs && j <= re)
387                                         pi.pain.fillRectangle(xx, y, w, h,
388                                                               LColor::selection);
389                                 xx += w;
390
391                         }
392                         y += h;
393                 }
394
395         } else {
396                 cur.text()->drawSelection(pi, x + getCellXPos(cur.idx()) + tabular.getBeginningOfTextInCell(cur.idx()), 0 /*this value is ignored */);
397         }
398 }
399
400
401 void InsetTabular::drawCellLines(Painter & pain, int x, int y,
402                                  row_type row, idx_type cell, bool erased) const
403 {
404         int x2 = x + tabular.getWidthOfColumn(cell);
405         bool on_off = false;
406         LColor::color col = LColor::tabularline;
407         LColor::color onoffcol = LColor::tabularonoffline;
408
409         if (erased) {
410                 col = LColor::strikeout;
411                 onoffcol = LColor::strikeout;
412         }
413
414         if (!tabular.topAlreadyDrawn(cell)) {
415                 on_off = !tabular.topLine(cell);
416                 pain.line(x, y - tabular.getAscentOfRow(row),
417                           x2, y -  tabular.getAscentOfRow(row),
418                           on_off ? onoffcol : col,
419                           on_off ? Painter::line_onoffdash : Painter::line_solid);
420         }
421         on_off = !tabular.bottomLine(cell);
422         pain.line(x, y + tabular.getDescentOfRow(row),
423                   x2, y + tabular.getDescentOfRow(row),
424                   on_off ? onoffcol : col,
425                   on_off ? Painter::line_onoffdash : Painter::line_solid);
426         if (!tabular.leftAlreadyDrawn(cell)) {
427                 on_off = !tabular.leftLine(cell);
428                 pain.line(x, y -  tabular.getAscentOfRow(row),
429                           x, y +  tabular.getDescentOfRow(row),
430                           on_off ? onoffcol : col,
431                           on_off ? Painter::line_onoffdash : Painter::line_solid);
432         }
433         on_off = !tabular.rightLine(cell);
434         pain.line(x2 - tabular.getAdditionalWidth(cell),
435                   y -  tabular.getAscentOfRow(row),
436                   x2 - tabular.getAdditionalWidth(cell),
437                   y +  tabular.getDescentOfRow(row),
438                   on_off ? onoffcol : col,
439                   on_off ? Painter::line_onoffdash : Painter::line_solid);
440 }
441
442
443 string const InsetTabular::editMessage() const
444 {
445         return _("Opened table");
446 }
447
448
449 void InsetTabular::edit(LCursor & cur, bool left)
450 {
451         //lyxerr << "InsetTabular::edit: " << this << endl;
452         finishUndo();
453         cur.selection() = false;
454         cur.push(*this);
455         if (left) {
456                 if (isRightToLeft(cur))
457                         cur.idx() = tabular.getLastCellInRow(0);
458                 else
459                         cur.idx() = 0;
460                 cur.pit() = 0;
461                 cur.pos() = 0;
462         } else {
463                 if (isRightToLeft(cur))
464                         cur.idx() = tabular.getFirstCellInRow(tabular.rows() - 1);
465                 else
466                         cur.idx() = tabular.getNumberOfCells() - 1;
467                 cur.pit() = 0;
468                 cur.pos() = cur.lastpos(); // FIXME crude guess
469         }
470         // this accesses the position cache before it is initialized
471         //resetPos(cur);
472         //cur.bv().fitCursor();
473 }
474
475
476 void InsetTabular::doDispatch(LCursor & cur, FuncRequest & cmd)
477 {
478         lyxerr[Debug::DEBUG] << "# InsetTabular::doDispatch: cmd: " << cmd
479                              << "\n  cur:" << cur << endl;
480         CursorSlice sl = cur.top();
481         LCursor & bvcur = cur.bv().cursor();
482
483         switch (cmd.action) {
484
485         case LFUN_MOUSE_PRESS:
486                 //lyxerr << "# InsetTabular::MousePress\n" << cur.bv().cursor() << endl;
487
488                 if (cmd.button() == mouse_button::button1 
489                     || cmd.button() == mouse_button::button3) {
490                         cur.selection() = false;
491                         setCursorFromCoordinates(cur, cmd.x, cmd.y);
492                         cur.resetAnchor();
493                         bvcur = cur;
494                         break;
495                 }
496
497                 if (cmd.button() == mouse_button::button2) {
498                         cmd = FuncRequest(LFUN_PRIMARY_SELECTION_PASTE, "paragraph");
499                         doDispatch(cur, cmd);
500                 }
501                 break;
502
503         case LFUN_MOUSE_MOTION:
504                 //lyxerr << "# InsetTabular::MouseMotion\n" << bvcur << endl;
505                 if (cmd.button() == mouse_button::button1) {
506                         // only accept motions to places not deeper nested than the real anchor
507                         if (bvcur.anchor_.hasPart(cur)) {
508                                 setCursorFromCoordinates(cur, cmd.x, cmd.y);
509                                 bvcur.setCursor(cur);
510                                 bvcur.selection() = true;
511                         } else
512                                 cur.undispatched();
513                 }
514                 break;
515
516         case LFUN_MOUSE_RELEASE:
517                 //lyxerr << "# InsetTabular::MouseRelease\n" << bvcur << endl;
518                 if (cmd.button() == mouse_button::button3)
519                         InsetTabularMailer(*this).showDialog(&cur.bv());
520                 break;
521
522         case LFUN_CELL_BACKWARD:
523                 movePrevCell(cur);
524                 cur.selection() = false;
525                 break;
526
527         case LFUN_CELL_FORWARD:
528                 moveNextCell(cur);
529                 cur.selection() = false;
530                 break;
531
532         case LFUN_CHAR_FORWARD_SELECT:
533         case LFUN_CHAR_FORWARD:
534                 cell(cur.idx())->dispatch(cur, cmd);
535                 if (!cur.result().dispatched()) {
536                         isRightToLeft(cur) ? movePrevCell(cur) : moveNextCell(cur);
537                         if (sl == cur.top())
538                                 cmd = FuncRequest(LFUN_FINISHED_RIGHT);
539                         else
540                                 cur.dispatched();
541                 }
542                 break;
543
544         case LFUN_CHAR_BACKWARD_SELECT:
545         case LFUN_CHAR_BACKWARD:
546                 cell(cur.idx())->dispatch(cur, cmd);
547                 if (!cur.result().dispatched()) {
548                         isRightToLeft(cur) ? moveNextCell(cur) : movePrevCell(cur);
549                         if (sl == cur.top())
550                                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
551                         else
552                                 cur.dispatched();
553                 }
554                 break;
555
556         case LFUN_DOWN_SELECT:
557         case LFUN_DOWN:
558                 cell(cur.idx())->dispatch(cur, cmd);
559                 cur.dispatched(); // override the cell's decision
560                 if (sl == cur.top())
561                         // if our LyXText didn't do anything to the cursor
562                         // then we try to put the cursor into the cell below
563                         // setting also the right targetX.
564                         if (tabular.row_of_cell(cur.idx()) != tabular.rows() - 1) {
565                                 cur.idx() = tabular.getCellBelow(cur.idx());
566                                 cur.pit() = 0;
567                                 cur.pos() = cell(cur.idx())->getText(0)->x2pos(
568                                         cur.pit(), 0, cur.targetX());
569                         }
570                 if (sl == cur.top()) {
571                         // we trick it to go to the RIGHT after leaving the
572                         // tabular.
573                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
574                         cur.undispatched();
575                 }
576                 break;
577
578         case LFUN_UP_SELECT:
579         case LFUN_UP:
580                 cell(cur.idx())->dispatch(cur, cmd);
581                 cur.dispatched(); // override the cell's decision
582                 if (sl == cur.top())
583                         // if our LyXText didn't do anything to the cursor
584                         // then we try to put the cursor into the cell above
585                         // setting also the right targetX.
586                         if (tabular.row_of_cell(cur.idx()) != 0) {
587                                 cur.idx() = tabular.getCellAbove(cur.idx());
588                                 cur.pit() = cur.lastpit();
589                                 LyXText const * text = cell(cur.idx())->getText(0);
590                                 cur.pos() = text->x2pos(
591                                         cur.pit(),
592                                         text->paragraphs().back().rows().size()-1,
593                                         cur.targetX());
594                         }
595                 if (sl == cur.top()) {
596                         cmd = FuncRequest(LFUN_FINISHED_UP);
597                         cur.undispatched();
598                 }
599                 break;
600
601 //      case LFUN_SCREEN_DOWN: {
602 //              //if (hasSelection())
603 //              //      cur.selection() = false;
604 //              col_type const col = tabular.column_of_cell(cur.idx());
605 //              int const t =   cur.bv().top_y() + cur.bv().painter().paperHeight();
606 //              if (t < yo() + tabular.getHeightOfTabular()) {
607 //                      cur.bv().scrollDocView(t);
608 //                      cur.idx() = tabular.getCellBelow(first_visible_cell) + col;
609 //              } else {
610 //                      cur.idx() = tabular.getFirstCellInRow(tabular.rows() - 1) + col;
611 //              }
612 //              cur.par() = 0;
613 //              cur.pos() = 0;
614 //              break;
615 //      }
616 //
617 //      case LFUN_SCREEN_UP: {
618 //              //if (hasSelection())
619 //              //      cur.selection() = false;
620 //              col_type const col = tabular.column_of_cell(cur.idx());
621 //              int const t =   cur.bv().top_y() + cur.bv().painter().paperHeight();
622 //              if (yo() < 0) {
623 //                      cur.bv().scrollDocView(t);
624 //                      if (yo() > 0)
625 //                              cur.idx() = col;
626 //                      else
627 //                              cur.idx() = tabular.getCellBelow(first_visible_cell) + col;
628 //              } else {
629 //                      cur.idx() = col;
630 //              }
631 //              cur.par() = cur.lastpar();
632 //              cur.pos() = cur.lastpos();
633 //              break;
634 //      }
635
636         case LFUN_LAYOUT_TABULAR:
637                 InsetTabularMailer(*this).showDialog(&cur.bv());
638                 break;
639
640         case LFUN_INSET_DIALOG_UPDATE:
641                 InsetTabularMailer(*this).updateDialog(&cur.bv());
642                 break;
643
644         case LFUN_TABULAR_FEATURE:
645                 if (!tabularFeatures(cur, cmd.argument))
646                         cur.undispatched();
647                 break;
648
649         // insert file functions
650         case LFUN_FILE_INSERT_ASCII_PARA:
651         case LFUN_FILE_INSERT_ASCII: {
652                 string const tmpstr = getContentsOfAsciiFile(&cur.bv(), cmd.argument, false);
653                 if (!tmpstr.empty() && !insertAsciiString(cur.bv(), tmpstr, false))
654                         cur.undispatched();
655                 break;
656         }
657
658         case LFUN_CUT:
659                 if (tablemode(cur)) {
660                         if (copySelection(cur)) {
661                                 recordUndoInset(cur, Undo::DELETE);
662                                 cutSelection(cur);
663                         }
664                 }
665                 else
666                         cell(cur.idx())->dispatch(cur, cmd);
667                 break;
668
669         case LFUN_CHAR_DELETE_BACKWARD:
670         case LFUN_CHAR_DELETE_FORWARD:
671                 if (tablemode(cur)) {
672                         recordUndoInset(cur, Undo::DELETE);
673                         cutSelection(cur);
674                 }
675                 else
676                         cell(cur.idx())->dispatch(cur, cmd);
677                 break;
678
679         case LFUN_COPY:
680                 if (!cur.selection())
681                         break;
682                 if (tablemode(cur)) {
683                         finishUndo();
684                         copySelection(cur);
685                 } else
686                         cell(cur.idx())->dispatch(cur, cmd);
687                 break;
688
689         case LFUN_PRIMARY_SELECTION_PASTE: {
690                 string const clip = cur.bv().owner()->gui().clipboard().get();
691                 if (clip.empty())
692                         break;
693                 // pass to InsertAsciiString, but
694                 // only if we have multi-cell content
695                 if (clip.find_first_of("\t\n") != string::npos) {
696                         if (insertAsciiString(cur.bv(), clip, false)) {
697                                 // content has been replaced,
698                                 // so cursor might be invalid
699                                 cur.pos() = cur.lastpos();
700                                 bvcur.setCursor(cur);
701                                 break;
702                         }
703                 } else {
704                         // so that the clipboard is used and it goes on
705                         // to default
706                         // and executes LFUN_PRIMARY_SELECTION_PASTE in insettext!
707                         paste_tabular.reset();
708                         dirtyTabularStack(false);
709                 }
710                 // fall through
711         }
712
713         case LFUN_PASTE:
714                 if (hasPasteBuffer() && tabularStackDirty()) {
715                         recordUndoInset(cur, Undo::INSERT);
716                         pasteSelection(cur);
717                         break;
718                 }
719                 cell(cur.idx())->dispatch(cur, cmd);
720                 // Reset pasted paragraphs:
721                 if (tabular.getPWidth(cur.idx()).zero())
722                         cell(cur.idx())->forceParagraphsToDefault(cur);
723                 break;
724
725         case LFUN_FONT_EMPH:
726         case LFUN_FONT_BOLD:
727         case LFUN_FONT_ROMAN:
728         case LFUN_FONT_NOUN:
729         case LFUN_FONT_ITAL:
730         case LFUN_FONT_FRAK:
731         case LFUN_FONT_CODE:
732         case LFUN_FONT_SANS:
733         case LFUN_FONT_FREE_APPLY:
734         case LFUN_FONT_FREE_UPDATE:
735         case LFUN_FONT_SIZE:
736         case LFUN_FONT_UNDERLINE:
737         case LFUN_LANGUAGE:
738         case LFUN_WORD_CAPITALIZE:
739         case LFUN_WORD_UPCASE:
740         case LFUN_WORD_LOWCASE:
741         case LFUN_CHARS_TRANSPOSE:
742                 if (tablemode(cur)) {
743                         row_type rs, re;
744                         col_type cs, ce;
745                         getSelection(cur, rs, re, cs, ce);
746                         for (row_type i = rs; i <= re; ++i) {
747                                 for (col_type j = cs; j <= ce; ++j) {
748                                         // cursor follows cell:
749                                         cur.idx() = tabular.getCellNumber(i, j);
750                                         // select this cell only:
751                                         cur.pos() = 0;
752                                         cur.resetAnchor();
753                                         cur.pos() = cur.top().lastpos();
754                                         cur.setCursor(cur);
755                                         cur.setSelection();
756                                         cell(cur.idx())->dispatch(cur, cmd);
757                                 }
758                         }
759                         // Restore original selection
760                         cur.idx() = tabular.getCellNumber(rs, cs);
761                         cur.pos() = 0;
762                         cur.resetAnchor();
763                         cur.idx() = tabular.getCellNumber(re, ce);
764                         cur.pos() = cur.top().lastpos();
765                         cur.setCursor(cur);
766                         cur.setSelection();
767                         break;
768                 } else {
769                         cell(cur.idx())->dispatch(cur, cmd);
770                         break;
771                 }
772         default:
773                 // we try to handle this event in the insets dispatch function.
774                 cell(cur.idx())->dispatch(cur, cmd);
775                 break;
776         }
777
778         resetPos(cur);
779         InsetTabularMailer(*this).updateDialog(&cur.bv());
780 }
781
782
783 // function sets an object as defined in func_status.h:
784 // states OK, Unknown, Disabled, On, Off.
785 bool InsetTabular::getStatus(LCursor & cur, FuncRequest const & cmd,
786         FuncStatus & status) const
787 {
788         switch (cmd.action) {
789         case LFUN_TABULAR_FEATURE: {
790                 int action = LyXTabular::LAST_ACTION;
791                 int i = 0;
792                 for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
793                         string const tmp = tabularFeature[i].feature;
794                         if (tmp == cmd.argument.substr(0, tmp.length())) {
795                                 action = tabularFeature[i].action;
796                                 break;
797                         }
798                 }
799                 if (action == LyXTabular::LAST_ACTION) {
800                         status.clear();
801                         status.unknown(true);
802                         return true;
803                 }
804
805                 string const argument
806                         = ltrim(cmd.argument.substr(tabularFeature[i].feature.length()));
807
808                 row_type sel_row_start = 0;
809                 row_type sel_row_end = 0;
810                 col_type dummy;
811                 LyXTabular::ltType dummyltt;
812                 bool flag = true;
813
814                 getSelection(cur, sel_row_start, sel_row_end, dummy, dummy);
815
816                 switch (action) {
817                 case LyXTabular::SET_PWIDTH:
818                 case LyXTabular::SET_MPWIDTH:
819                 case LyXTabular::SET_SPECIAL_COLUMN:
820                 case LyXTabular::SET_SPECIAL_MULTI:
821                 case LyXTabular::APPEND_ROW:
822                 case LyXTabular::APPEND_COLUMN:
823                 case LyXTabular::DELETE_ROW:
824                 case LyXTabular::DELETE_COLUMN:
825                 case LyXTabular::SET_ALL_LINES:
826                 case LyXTabular::UNSET_ALL_LINES:
827                 case LyXTabular::SET_TOP_SPACE:
828                 case LyXTabular::SET_BOTTOM_SPACE:
829                 case LyXTabular::SET_INTERLINE_SPACE:
830                         status.clear();
831                         return true;
832
833                 case LyXTabular::MULTICOLUMN:
834                         status.setOnOff(tabular.isMultiColumn(cur.idx()));
835                         break;
836
837                 case LyXTabular::M_TOGGLE_LINE_TOP:
838                         flag = false;
839                 case LyXTabular::TOGGLE_LINE_TOP:
840                         status.setOnOff(tabular.topLine(cur.idx(), flag));
841                         break;
842
843                 case LyXTabular::M_TOGGLE_LINE_BOTTOM:
844                         flag = false;
845                 case LyXTabular::TOGGLE_LINE_BOTTOM:
846                         status.setOnOff(tabular.bottomLine(cur.idx(), flag));
847                         break;
848
849                 case LyXTabular::M_TOGGLE_LINE_LEFT:
850                         flag = false;
851                 case LyXTabular::TOGGLE_LINE_LEFT:
852                         status.setOnOff(tabular.leftLine(cur.idx(), flag));
853                         break;
854
855                 case LyXTabular::M_TOGGLE_LINE_RIGHT:
856                         flag = false;
857                 case LyXTabular::TOGGLE_LINE_RIGHT:
858                         status.setOnOff(tabular.rightLine(cur.idx(), flag));
859                         break;
860
861                 case LyXTabular::M_ALIGN_LEFT:
862                         flag = false;
863                 case LyXTabular::ALIGN_LEFT:
864                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_LEFT);
865                         break;
866
867                 case LyXTabular::M_ALIGN_RIGHT:
868                         flag = false;
869                 case LyXTabular::ALIGN_RIGHT:
870                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_RIGHT);
871                         break;
872
873                 case LyXTabular::M_ALIGN_CENTER:
874                         flag = false;
875                 case LyXTabular::ALIGN_CENTER:
876                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_CENTER);
877                         break;
878
879                 case LyXTabular::ALIGN_BLOCK:
880                         status.enabled(!tabular.getPWidth(cur.idx()).zero());
881                         status.setOnOff(tabular.getAlignment(cur.idx(), flag) == LYX_ALIGN_BLOCK);
882                         break;
883
884                 case LyXTabular::M_VALIGN_TOP:
885                         flag = false;
886                 case LyXTabular::VALIGN_TOP:
887                         status.setOnOff(
888                                 tabular.getVAlignment(cur.idx(), flag) == LyXTabular::LYX_VALIGN_TOP);
889                         break;
890
891                 case LyXTabular::M_VALIGN_BOTTOM:
892                         flag = false;
893                 case LyXTabular::VALIGN_BOTTOM:
894                         status.setOnOff(
895                                 tabular.getVAlignment(cur.idx(), flag) == LyXTabular::LYX_VALIGN_BOTTOM);
896                         break;
897
898                 case LyXTabular::M_VALIGN_MIDDLE:
899                         flag = false;
900                 case LyXTabular::VALIGN_MIDDLE:
901                         status.setOnOff(
902                                 tabular.getVAlignment(cur.idx(), flag) == LyXTabular::LYX_VALIGN_MIDDLE);
903                         break;
904
905                 case LyXTabular::SET_LONGTABULAR:
906                         status.setOnOff(tabular.isLongTabular());
907                         break;
908
909                 case LyXTabular::UNSET_LONGTABULAR:
910                         status.setOnOff(!tabular.isLongTabular());
911                         break;
912
913                 case LyXTabular::SET_ROTATE_TABULAR:
914                         status.setOnOff(tabular.getRotateTabular());
915                         break;
916
917                 case LyXTabular::UNSET_ROTATE_TABULAR:
918                         status.setOnOff(!tabular.getRotateTabular());
919                         break;
920
921                 case LyXTabular::SET_ROTATE_CELL:
922                         status.setOnOff(tabular.getRotateCell(cur.idx()));
923                         break;
924
925                 case LyXTabular::UNSET_ROTATE_CELL:
926                         status.setOnOff(!tabular.getRotateCell(cur.idx()));
927                         break;
928
929                 case LyXTabular::SET_USEBOX:
930                         status.setOnOff(convert<int>(argument) == tabular.getUsebox(cur.idx()));
931                         break;
932
933                 case LyXTabular::SET_LTFIRSTHEAD:
934                         status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
935                         break;
936
937                 case LyXTabular::UNSET_LTFIRSTHEAD:
938                         status.setOnOff(!tabular.getRowOfLTHead(sel_row_start, dummyltt));
939                         break;
940
941                 case LyXTabular::SET_LTHEAD:
942                         status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
943                         break;
944
945                 case LyXTabular::UNSET_LTHEAD:
946                         status.setOnOff(!tabular.getRowOfLTHead(sel_row_start, dummyltt));
947                         break;
948
949                 case LyXTabular::SET_LTFOOT:
950                         status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
951                         break;
952
953                 case LyXTabular::UNSET_LTFOOT:
954                         status.setOnOff(!tabular.getRowOfLTFoot(sel_row_start, dummyltt));
955                         break;
956
957                 case LyXTabular::SET_LTLASTFOOT:
958                         status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
959                         break;
960
961                 case LyXTabular::UNSET_LTLASTFOOT:
962                         status.setOnOff(!tabular.getRowOfLTFoot(sel_row_start, dummyltt));
963                         break;
964
965                 case LyXTabular::SET_LTNEWPAGE:
966                         status.setOnOff(tabular.getLTNewPage(sel_row_start));
967                         break;
968                         
969                 case LyXTabular::SET_BOOKTABS:
970                         status.setOnOff(tabular.useBookTabs());
971                         break;
972                         
973                 case LyXTabular::UNSET_BOOKTABS:
974                         status.setOnOff(!tabular.useBookTabs());
975                         break;
976
977                 default:
978                         status.clear();
979                         status.enabled(false);
980                         break;
981                 }
982                 return true;
983         }
984
985         // These are only enabled inside tabular
986         case LFUN_CELL_BACKWARD:
987         case LFUN_CELL_FORWARD:
988                 status.enabled(true);
989                 return true;
990
991         // disable these with multiple cells selected
992         case LFUN_INSET_INSERT:
993         case LFUN_TABULAR_INSERT:
994         case LFUN_CHARSTYLE_INSERT:
995         case LFUN_FLOAT_INSERT:
996         case LFUN_FLOAT_WIDE_INSERT:
997         case LFUN_FOOTNOTE_INSERT:
998         case LFUN_MARGINALNOTE_INSERT:
999         case LFUN_MATH_INSERT:
1000         case LFUN_MATH_MODE:
1001         case LFUN_MATH_MUTATE:
1002         case LFUN_MATH_DISPLAY:
1003         case LFUN_NOTE_INSERT:
1004         case LFUN_OPTIONAL_INSERT:
1005         case LFUN_BOX_INSERT:
1006         case LFUN_BRANCH_INSERT:
1007         case LFUN_WRAP_INSERT:
1008         case LFUN_ERT_INSERT: {
1009                 if (tablemode(cur)) {
1010                         status.enabled(false);
1011                         return true;
1012                 } else
1013                         return cell(cur.idx())->getStatus(cur, cmd, status);
1014         }
1015
1016         // disable in non-fixed-width cells
1017         case LFUN_BREAK_LINE:
1018         case LFUN_BREAK_PARAGRAPH:
1019         case LFUN_BREAK_PARAGRAPH_KEEP_LAYOUT:
1020         case LFUN_BREAK_PARAGRAPH_SKIP: {
1021                 if (tabular.getPWidth(cur.idx()).zero()) {
1022                         status.enabled(false);
1023                         return true;
1024                 } else
1025                         return cell(cur.idx())->getStatus(cur, cmd, status);
1026         }
1027
1028         case LFUN_PASTE:
1029                 if (tabularStackDirty()) {
1030                         status.enabled(true);
1031                         return true;
1032                 }
1033
1034         case LFUN_INSET_MODIFY:
1035                 if (translate(cmd.getArg(0)) == TABULAR_CODE) {
1036                         status.enabled(true);
1037                         return true;
1038                 }
1039                 // Fall through
1040
1041         default:
1042                 // we try to handle this event in the insets dispatch function.
1043                 return cell(cur.idx())->getStatus(cur, cmd, status);
1044         }
1045 }
1046
1047
1048 int InsetTabular::latex(Buffer const & buf, ostream & os,
1049                         OutputParams const & runparams) const
1050 {
1051         return tabular.latex(buf, os, runparams);
1052 }
1053
1054
1055 int InsetTabular::plaintext(Buffer const & buf, ostream & os,
1056                         OutputParams const & runparams) const
1057 {
1058         int const dp = runparams.linelen ? runparams.depth : 0;
1059         return tabular.plaintext(buf, os, runparams, dp, false, 0);
1060 }
1061
1062
1063 int InsetTabular::linuxdoc(Buffer const & buf, ostream & os,
1064                            OutputParams const & runparams) const
1065 {
1066         return tabular.linuxdoc(buf,os, runparams);
1067 }
1068
1069
1070 int InsetTabular::docbook(Buffer const & buf, ostream & os,
1071                           OutputParams const & runparams) const
1072 {
1073         int ret = 0;
1074         InsetBase * master = 0;
1075
1076 #ifdef WITH_WARNINGS
1077 #warning Why not pass a proper DocIterator here?
1078 #endif
1079 #if 0
1080         // if the table is inside a float it doesn't need the informaltable
1081         // wrapper. Search for it.
1082         for (master = owner(); master; master = master->owner())
1083                 if (master->lyxCode() == InsetBase::FLOAT_CODE)
1084                         break;
1085 #endif
1086
1087         if (!master) {
1088                 os << "<informaltable>";
1089                 ++ret;
1090         }
1091         ret += tabular.docbook(buf, os, runparams);
1092         if (!master) {
1093                 os << "</informaltable>";
1094                 ++ret;
1095         }
1096         return ret;
1097 }
1098
1099
1100 void InsetTabular::validate(LaTeXFeatures & features) const
1101 {
1102         tabular.validate(features);
1103 }
1104
1105
1106 shared_ptr<InsetText const> InsetTabular::cell(idx_type idx) const
1107 {
1108         return tabular.getCellInset(idx);
1109 }
1110
1111
1112 shared_ptr<InsetText> InsetTabular::cell(idx_type idx)
1113 {
1114         return tabular.getCellInset(idx);
1115 }
1116
1117
1118 void InsetTabular::cursorPos
1119         (CursorSlice const & sl, bool boundary, int & x, int & y) const
1120 {
1121         cell(sl.idx())->cursorPos(sl, boundary, x, y);
1122
1123         // y offset     correction
1124         int const row = tabular.row_of_cell(sl.idx());
1125         for (int i = 0; i <= row; ++i) {
1126                 if (i != 0) {
1127                         y += tabular.getAscentOfRow(i);
1128                         y += tabular.getAdditionalHeight(i);
1129                 }
1130                 if (i != row)
1131                         y += tabular.getDescentOfRow(i);
1132         }
1133
1134         // x offset correction
1135         int const col = tabular.column_of_cell(sl.idx());
1136         int idx = tabular.getCellNumber(row, 0);
1137         for (int j = 0; j < col; ++j) {
1138                 if (tabular.isPartOfMultiColumn(row, j))
1139                         continue;
1140                 x += tabular.getWidthOfColumn(idx);
1141                 ++idx;
1142         }
1143         x += tabular.getBeginningOfTextInCell(idx);
1144         x += ADD_TO_TABULAR_WIDTH;
1145         x += scx_;
1146 }
1147
1148
1149 int InsetTabular::dist(idx_type const cell, int x, int y) const
1150 {
1151         int xx = 0;
1152         int yy = 0;
1153         InsetBase const & inset = *tabular.getCellInset(cell);
1154         Point o = theCoords.getInsets().xy(&inset);
1155         int const xbeg = o.x_ - tabular.getBeginningOfTextInCell(cell);
1156         int const xend = xbeg + tabular.getWidthOfColumn(cell);
1157         row_type const row = tabular.row_of_cell(cell);
1158         int const ybeg = o.y_ - tabular.getAscentOfRow(row) -
1159                          tabular.getAdditionalHeight(row);
1160         int const yend = o.y_ + tabular.getDescentOfRow(row);
1161
1162         if (x < xbeg)
1163                 xx = xbeg - x;
1164         else if (x > xend)
1165                 xx = x - xend;
1166
1167         if (y < ybeg)
1168                 yy = ybeg - y;
1169         else if (y > yend)
1170                 yy = y - yend;
1171
1172         //lyxerr << " xbeg=" << xbeg << "  xend=" << xend
1173         //       << " ybeg=" << ybeg << " yend=" << yend
1174         //       << " xx=" << xx << " yy=" << yy
1175         //       << " dist=" << xx + yy << endl;
1176         return xx + yy;
1177 }
1178
1179
1180 InsetBase * InsetTabular::editXY(LCursor & cur, int x, int y)
1181 {
1182         //lyxerr << "InsetTabular::editXY: " << this << endl;
1183         cur.selection() = false;
1184         cur.push(*this);
1185         cur.idx() = getNearestCell(x, y);
1186         resetPos(cur);
1187         return cell(cur.idx())->text_.editXY(cur, x, y);
1188 }
1189
1190
1191 void InsetTabular::setCursorFromCoordinates(LCursor & cur, int x, int y) const
1192 {
1193         cur.idx() = getNearestCell(x, y);
1194         cell(cur.idx())->text_.setCursorFromCoordinates(cur, x, y);
1195 }
1196
1197
1198 InsetTabular::idx_type InsetTabular::getNearestCell(int x, int y) const
1199 {
1200         idx_type idx_min = 0;
1201         int dist_min = std::numeric_limits<int>::max();
1202         for (idx_type i = 0, n = nargs(); i != n; ++i) {
1203                 if (theCoords.getInsets().has(tabular.getCellInset(i).get())) {
1204                         int const d = dist(i, x, y);
1205                         if (d < dist_min) {
1206                                 dist_min = d;
1207                                 idx_min = i;
1208                         }
1209                 }
1210         }
1211         return idx_min;
1212 }
1213
1214
1215 int InsetTabular::getCellXPos(idx_type const cell) const
1216 {
1217         idx_type c = cell;
1218
1219         for (; !tabular.isFirstCellInRow(c); --c)
1220                 ;
1221         int lx = tabular.getWidthOfColumn(cell);
1222         for (; c < cell; ++c)
1223                 lx += tabular.getWidthOfColumn(c);
1224
1225         return lx - tabular.getWidthOfColumn(cell);
1226 }
1227
1228
1229 void InsetTabular::resetPos(LCursor & cur) const
1230 {
1231         BufferView & bv = cur.bv();
1232         int const maxwidth = bv.workWidth();
1233
1234         if (&cur.inset() != this) {
1235                 scx_ = 0;
1236         } else {
1237                 int const X1 = 0;
1238                 int const X2 = maxwidth;
1239                 int const offset = ADD_TO_TABULAR_WIDTH + 2;
1240                 int const x1 = xo() + getCellXPos(cur.idx()) + offset;
1241                 int const x2 = x1 + tabular.getWidthOfColumn(cur.idx());
1242
1243                 if (x1 < X1)
1244                         scx_ = X1 + 20 - x1;
1245                 else if (x2 > X2)
1246                         scx_ = X2 - 20 - x2;
1247                 else
1248                         scx_ = 0;
1249         }
1250
1251         cur.needsUpdate();
1252
1253         InsetTabularMailer(*this).updateDialog(&bv);
1254 }
1255
1256
1257 void InsetTabular::moveNextCell(LCursor & cur)
1258 {
1259         if (isRightToLeft(cur)) {
1260                 if (tabular.isFirstCellInRow(cur.idx())) {
1261                         row_type const row = tabular.row_of_cell(cur.idx());
1262                         if (row == tabular.rows() - 1)
1263                                 return;
1264                         cur.idx() = tabular.getCellBelow(tabular.getLastCellInRow(row));
1265                 } else {
1266                         if (cur.idx() == 0)
1267                                 return;
1268                         --cur.idx();
1269                 }
1270         } else {
1271                 if (tabular.isLastCell(cur.idx()))
1272                         return;
1273                 ++cur.idx();
1274         }
1275         cur.pit() = 0;
1276         cur.pos() = 0;
1277         resetPos(cur);
1278 }
1279
1280
1281 void InsetTabular::movePrevCell(LCursor & cur)
1282 {
1283         if (isRightToLeft(cur)) {
1284                 if (tabular.isLastCellInRow(cur.idx())) {
1285                         row_type const row = tabular.row_of_cell(cur.idx());
1286                         if (row == 0)
1287                                 return;
1288                         cur.idx() = tabular.getFirstCellInRow(row);
1289                         cur.idx() = tabular.getCellAbove(cur.idx());
1290                 } else {
1291                         if (tabular.isLastCell(cur.idx()))
1292                                 return;
1293                         ++cur.idx();
1294                 }
1295         } else {
1296                 if (cur.idx() == 0) // first cell
1297                         return;
1298                 --cur.idx();
1299         }
1300         cur.pit() = cur.lastpit();
1301         cur.pos() = cur.lastpos();
1302         resetPos(cur);
1303 }
1304
1305
1306 bool InsetTabular::tabularFeatures(LCursor & cur, string const & what)
1307 {
1308         LyXTabular::Feature action = LyXTabular::LAST_ACTION;
1309
1310         int i = 0;
1311         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
1312                 string const tmp = tabularFeature[i].feature;
1313
1314                 if (tmp == what.substr(0, tmp.length())) {
1315                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
1316                         //tabularFeatures[i].feature.length())) {
1317                         action = tabularFeature[i].action;
1318                         break;
1319                 }
1320         }
1321         if (action == LyXTabular::LAST_ACTION)
1322                 return false;
1323
1324         string const val =
1325                 ltrim(what.substr(tabularFeature[i].feature.length()));
1326         tabularFeatures(cur, action, val);
1327         return true;
1328 }
1329
1330
1331 namespace {
1332
1333 void checkLongtableSpecial(LyXTabular::ltType & ltt,
1334                           string const & special, bool & flag)
1335 {
1336         if (special == "dl_above") {
1337                 ltt.topDL = flag;
1338                 ltt.set = false;
1339         } else if (special == "dl_below") {
1340                 ltt.bottomDL = flag;
1341                 ltt.set = false;
1342         } else if (special == "empty") {
1343                 ltt.empty = flag;
1344                 ltt.set = false;
1345         } else if (flag) {
1346                 ltt.empty = false;
1347                 ltt.set = true;
1348         }
1349 }
1350
1351 } // anon namespace
1352
1353
1354 void InsetTabular::tabularFeatures(LCursor & cur,
1355         LyXTabular::Feature feature, string const & value)
1356 {
1357         BufferView & bv = cur.bv();
1358         col_type sel_col_start;
1359         col_type sel_col_end;
1360         row_type sel_row_start;
1361         row_type sel_row_end;
1362         bool setLines = false;
1363         LyXAlignment setAlign = LYX_ALIGN_LEFT;
1364         LyXTabular::VAlignment setVAlign = LyXTabular::LYX_VALIGN_TOP;
1365
1366         switch (feature) {
1367
1368         case LyXTabular::M_ALIGN_LEFT:
1369         case LyXTabular::ALIGN_LEFT:
1370                 setAlign = LYX_ALIGN_LEFT;
1371                 break;
1372
1373         case LyXTabular::M_ALIGN_RIGHT:
1374         case LyXTabular::ALIGN_RIGHT:
1375                 setAlign = LYX_ALIGN_RIGHT;
1376                 break;
1377
1378         case LyXTabular::M_ALIGN_CENTER:
1379         case LyXTabular::ALIGN_CENTER:
1380                 setAlign = LYX_ALIGN_CENTER;
1381                 break;
1382
1383         case LyXTabular::ALIGN_BLOCK:
1384                 setAlign = LYX_ALIGN_BLOCK;
1385                 break;
1386
1387         case LyXTabular::M_VALIGN_TOP:
1388         case LyXTabular::VALIGN_TOP:
1389                 setVAlign = LyXTabular::LYX_VALIGN_TOP;
1390                 break;
1391
1392         case LyXTabular::M_VALIGN_BOTTOM:
1393         case LyXTabular::VALIGN_BOTTOM:
1394                 setVAlign = LyXTabular::LYX_VALIGN_BOTTOM;
1395                 break;
1396
1397         case LyXTabular::M_VALIGN_MIDDLE:
1398         case LyXTabular::VALIGN_MIDDLE:
1399                 setVAlign = LyXTabular::LYX_VALIGN_MIDDLE;
1400                 break;
1401
1402         default:
1403                 break;
1404         }
1405
1406         recordUndoInset(cur, Undo::ATOMIC);
1407
1408         getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
1409         row_type const row = tabular.row_of_cell(cur.idx());
1410         col_type const column = tabular.column_of_cell(cur.idx());
1411         bool flag = true;
1412         LyXTabular::ltType ltt;
1413
1414         switch (feature) {
1415
1416         case LyXTabular::SET_PWIDTH: {
1417                 LyXLength const len(value);
1418                 tabular.setColumnPWidth(cur, cur.idx(), len);
1419                 if (len.zero()
1420                     && tabular.getAlignment(cur.idx(), true) == LYX_ALIGN_BLOCK)
1421                         tabularFeatures(cur, LyXTabular::ALIGN_CENTER, string());
1422                 break;
1423         }
1424
1425         case LyXTabular::SET_MPWIDTH:
1426                 tabular.setMColumnPWidth(cur, cur.idx(), LyXLength(value));
1427                 break;
1428
1429         case LyXTabular::SET_SPECIAL_COLUMN:
1430         case LyXTabular::SET_SPECIAL_MULTI:
1431                 tabular.setAlignSpecial(cur.idx(),value,feature);
1432                 break;
1433
1434         case LyXTabular::APPEND_ROW:
1435                 // append the row into the tabular
1436                 tabular.appendRow(bv.buffer()->params(), cur.idx());
1437                 break;
1438
1439         case LyXTabular::APPEND_COLUMN:
1440                 // append the column into the tabular
1441                 tabular.appendColumn(bv.buffer()->params(), cur.idx());
1442                 cur.idx() = tabular.getCellNumber(row, column);
1443                 break;
1444
1445         case LyXTabular::DELETE_ROW:
1446                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1447                         tabular.deleteRow(sel_row_start);
1448                 if (sel_row_start >= tabular.rows())
1449                         --sel_row_start;
1450                 cur.idx() = tabular.getCellNumber(sel_row_start, column);
1451                 cur.pit() = 0;
1452                 cur.pos() = 0;
1453                 cur.selection() = false;
1454                 break;
1455
1456         case LyXTabular::DELETE_COLUMN:
1457                 for (col_type i = sel_col_start; i <= sel_col_end; ++i)
1458                         tabular.deleteColumn(sel_col_start);
1459                 if (sel_col_start >= tabular.columns())
1460                         --sel_col_start;
1461                 cur.idx() = tabular.getCellNumber(row, sel_col_start);
1462                 cur.pit() = 0;
1463                 cur.pos() = 0;
1464                 cur.selection() = false;
1465                 break;
1466
1467         case LyXTabular::M_TOGGLE_LINE_TOP:
1468                 flag = false;
1469         case LyXTabular::TOGGLE_LINE_TOP: {
1470                 bool lineSet = !tabular.topLine(cur.idx(), flag);
1471                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1472                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1473                                 tabular.setTopLine(
1474                                         tabular.getCellNumber(i, j),
1475                                         lineSet, flag);
1476                 break;
1477         }
1478
1479         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
1480                 flag = false;
1481         case LyXTabular::TOGGLE_LINE_BOTTOM: {
1482                 bool lineSet = !tabular.bottomLine(cur.idx(), flag);
1483                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1484                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1485                                 tabular.setBottomLine(
1486                                         tabular.getCellNumber(i, j),
1487                                         lineSet,
1488                                         flag);
1489                 break;
1490         }
1491
1492         case LyXTabular::M_TOGGLE_LINE_LEFT:
1493                 flag = false;
1494         case LyXTabular::TOGGLE_LINE_LEFT: {
1495                 bool lineSet = !tabular.leftLine(cur.idx(), flag);
1496                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1497                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1498                                 tabular.setLeftLine(
1499                                         tabular.getCellNumber(i,j),
1500                                         lineSet,
1501                                         flag);
1502                 break;
1503         }
1504
1505         case LyXTabular::M_TOGGLE_LINE_RIGHT:
1506                 flag = false;
1507         case LyXTabular::TOGGLE_LINE_RIGHT: {
1508                 bool lineSet = !tabular.rightLine(cur.idx(), flag);
1509                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1510                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1511                                 tabular.setRightLine(
1512                                         tabular.getCellNumber(i,j),
1513                                         lineSet,
1514                                         flag);
1515                 break;
1516         }
1517
1518         case LyXTabular::M_ALIGN_LEFT:
1519         case LyXTabular::M_ALIGN_RIGHT:
1520         case LyXTabular::M_ALIGN_CENTER:
1521                 flag = false;
1522         case LyXTabular::ALIGN_LEFT:
1523         case LyXTabular::ALIGN_RIGHT:
1524         case LyXTabular::ALIGN_CENTER:
1525         case LyXTabular::ALIGN_BLOCK:
1526                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1527                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1528                                 tabular.setAlignment(
1529                                         tabular.getCellNumber(i, j),
1530                                         setAlign,
1531                                         flag);
1532                 break;
1533
1534         case LyXTabular::M_VALIGN_TOP:
1535         case LyXTabular::M_VALIGN_BOTTOM:
1536         case LyXTabular::M_VALIGN_MIDDLE:
1537                 flag = false;
1538         case LyXTabular::VALIGN_TOP:
1539         case LyXTabular::VALIGN_BOTTOM:
1540         case LyXTabular::VALIGN_MIDDLE:
1541                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1542                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1543                                 tabular.setVAlignment(
1544                                         tabular.getCellNumber(i, j),
1545                                         setVAlign, flag);
1546                 break;
1547
1548         case LyXTabular::MULTICOLUMN: {
1549                 if (sel_row_start != sel_row_end) {
1550 #ifdef WITH_WARNINGS
1551 #warning Need I say it ? This is horrible.
1552 #endif
1553                         Alert::error(_("Error setting multicolumn"),
1554                                    _("You cannot set multicolumn vertically."));
1555                         return;
1556                 }
1557                 if (!cur.selection()) {
1558                         // just multicol for one single cell
1559                         // check whether we are completely in a multicol
1560                         if (tabular.isMultiColumn(cur.idx()))
1561                                 tabular.unsetMultiColumn(cur.idx());
1562                         else
1563                                 tabular.setMultiColumn(bv.buffer(), cur.idx(), 1);
1564                         break;
1565                 }
1566                 // we have a selection so this means we just add all this
1567                 // cells to form a multicolumn cell
1568                 idx_type const s_start = cur.selBegin().idx();
1569                 idx_type const s_end = cur.selEnd().idx();
1570                 tabular.setMultiColumn(bv.buffer(), s_start, s_end - s_start + 1);
1571                 cur.idx() = s_start;
1572                 cur.pit() = 0;
1573                 cur.pos() = 0;
1574                 cur.selection() = false;
1575                 break;
1576         }
1577
1578         case LyXTabular::SET_ALL_LINES:
1579                 setLines = true;
1580         case LyXTabular::UNSET_ALL_LINES:
1581                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1582                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1583                                 tabular.setAllLines(
1584                                         tabular.getCellNumber(i,j), setLines);
1585                 break;
1586
1587         case LyXTabular::SET_LONGTABULAR:
1588                 tabular.setLongTabular(true);
1589                 break;
1590
1591         case LyXTabular::UNSET_LONGTABULAR:
1592                 tabular.setLongTabular(false);
1593                 break;
1594
1595         case LyXTabular::SET_ROTATE_TABULAR:
1596                 tabular.setRotateTabular(true);
1597                 break;
1598
1599         case LyXTabular::UNSET_ROTATE_TABULAR:
1600                 tabular.setRotateTabular(false);
1601                 break;
1602
1603         case LyXTabular::SET_ROTATE_CELL:
1604                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1605                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1606                                 tabular.setRotateCell(
1607                                         tabular.getCellNumber(i, j), true);
1608                 break;
1609
1610         case LyXTabular::UNSET_ROTATE_CELL:
1611                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1612                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1613                                 tabular.setRotateCell(
1614                                         tabular.getCellNumber(i, j), false);
1615                 break;
1616
1617         case LyXTabular::SET_USEBOX: {
1618                 LyXTabular::BoxType val = LyXTabular::BoxType(convert<int>(value));
1619                 if (val == tabular.getUsebox(cur.idx()))
1620                         val = LyXTabular::BOX_NONE;
1621                 for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1622                         for (col_type j = sel_col_start; j <= sel_col_end; ++j)
1623                                 tabular.setUsebox(tabular.getCellNumber(i, j), val);
1624                 break;
1625         }
1626
1627         case LyXTabular::UNSET_LTFIRSTHEAD:
1628                 flag = false;
1629         case LyXTabular::SET_LTFIRSTHEAD:
1630                 tabular.getRowOfLTFirstHead(row, ltt);
1631                 checkLongtableSpecial(ltt, value, flag);
1632                 tabular.setLTHead(row, flag, ltt, true);
1633                 break;
1634
1635         case LyXTabular::UNSET_LTHEAD:
1636                 flag = false;
1637         case LyXTabular::SET_LTHEAD:
1638                 tabular.getRowOfLTHead(row, ltt);
1639                 checkLongtableSpecial(ltt, value, flag);
1640                 tabular.setLTHead(row, flag, ltt, false);
1641                 break;
1642
1643         case LyXTabular::UNSET_LTFOOT:
1644                 flag = false;
1645         case LyXTabular::SET_LTFOOT:
1646                 tabular.getRowOfLTFoot(row, ltt);
1647                 checkLongtableSpecial(ltt, value, flag);
1648                 tabular.setLTFoot(row, flag, ltt, false);
1649                 break;
1650
1651         case LyXTabular::UNSET_LTLASTFOOT:
1652                 flag = false;
1653         case LyXTabular::SET_LTLASTFOOT:
1654                 tabular.getRowOfLTLastFoot(row, ltt);
1655                 checkLongtableSpecial(ltt, value, flag);
1656                 tabular.setLTFoot(row, flag, ltt, true);
1657                 break;
1658
1659         case LyXTabular::SET_LTNEWPAGE:
1660                 tabular.setLTNewPage(row, !tabular.getLTNewPage(row));
1661                 break;
1662
1663         case LyXTabular::SET_BOOKTABS:
1664                 tabular.setBookTabs(true);
1665                 break;
1666
1667         case LyXTabular::UNSET_BOOKTABS:
1668                 tabular.setBookTabs(false);
1669                 break;
1670
1671         case LyXTabular::SET_TOP_SPACE: {
1672                 LyXLength len;
1673                 if (value == "default")
1674                         for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1675                                 tabular.row_info[i].top_space_default = true;
1676                 else if (isValidLength(value, &len))
1677                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
1678                                 tabular.row_info[i].top_space_default = false;
1679                                 tabular.row_info[i].top_space = len;
1680                         }
1681                 else
1682                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
1683                                 tabular.row_info[i].top_space_default = false;
1684                                 tabular.row_info[i].top_space = len;
1685                         }
1686                 break;
1687         }
1688
1689         case LyXTabular::SET_BOTTOM_SPACE: {
1690                 LyXLength len;
1691                 if (value == "default")
1692                         for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1693                                 tabular.row_info[i].bottom_space_default = true;
1694                 else if (isValidLength(value, &len))
1695                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
1696                                 tabular.row_info[i].bottom_space_default = false;
1697                                 tabular.row_info[i].bottom_space = len;
1698                         }
1699                 else
1700                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
1701                                 tabular.row_info[i].bottom_space_default = false;
1702                                 tabular.row_info[i].bottom_space = len;
1703                         }
1704                 break;
1705         }
1706
1707         case LyXTabular::SET_INTERLINE_SPACE: {
1708                 LyXLength len;
1709                 if (value == "default")
1710                         for (row_type i = sel_row_start; i <= sel_row_end; ++i)
1711                                 tabular.row_info[i].interline_space_default = true;
1712                 else if (isValidLength(value, &len))
1713                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
1714                                 tabular.row_info[i].interline_space_default = false;
1715                                 tabular.row_info[i].interline_space = len;
1716                         }
1717                 else
1718                         for (row_type i = sel_row_start; i <= sel_row_end; ++i) {
1719                                 tabular.row_info[i].interline_space_default = false;
1720                                 tabular.row_info[i].interline_space = len;
1721                         }
1722                 break;
1723         }
1724
1725         // dummy stuff just to avoid warnings
1726         case LyXTabular::LAST_ACTION:
1727                 break;
1728         }
1729
1730         InsetTabularMailer(*this).updateDialog(&bv);
1731 }
1732
1733
1734 bool InsetTabular::showInsetDialog(BufferView * bv) const
1735 {
1736         InsetTabularMailer(*this).showDialog(bv);
1737         return true;
1738 }
1739
1740
1741 void InsetTabular::openLayoutDialog(BufferView * bv) const
1742 {
1743         InsetTabularMailer(*this).showDialog(bv);
1744 }
1745
1746
1747 bool InsetTabular::copySelection(LCursor & cur)
1748 {
1749         if (!cur.selection())
1750                 return false;
1751
1752         row_type rs, re;
1753         col_type cs, ce;
1754         getSelection(cur, rs, re, cs, ce);
1755
1756         paste_tabular.reset(new LyXTabular(tabular));
1757
1758         for (row_type i = 0; i < rs; ++i)
1759                 paste_tabular->deleteRow(0);
1760
1761         row_type const rows = re - rs + 1;
1762         while (paste_tabular->rows() > rows)
1763                 paste_tabular->deleteRow(rows);
1764
1765         paste_tabular->setTopLine(0, true, true);
1766         paste_tabular->setBottomLine(paste_tabular->getFirstCellInRow(rows - 1),
1767                                      true, true);
1768
1769         for (col_type i = 0; i < cs; ++i)
1770                 paste_tabular->deleteColumn(0);
1771
1772         col_type const columns = ce - cs + 1;
1773         while (paste_tabular->columns() > columns)
1774                 paste_tabular->deleteColumn(columns);
1775
1776         paste_tabular->setLeftLine(0, true, true);
1777         paste_tabular->setRightLine(paste_tabular->getLastCellInRow(0),
1778                                     true, true);
1779
1780         ostringstream os;
1781         OutputParams const runparams;
1782         paste_tabular->plaintext(cur.buffer(), os, runparams, 0, true, '\t');
1783         cur.bv().owner()->gui().clipboard().put(os.str());
1784         // mark tabular stack dirty
1785         // FIXME: this is a workaround for bug 1919. Should be removed for 1.5,
1786         // when we (hopefully) have a one-for-all paste mechanism.
1787         lyx::cap::dirtyTabularStack(true);
1788
1789         return true;
1790 }
1791
1792
1793 bool InsetTabular::pasteSelection(LCursor & cur)
1794 {
1795         if (!paste_tabular)
1796                 return false;
1797         col_type const actcol = tabular.column_of_cell(cur.idx());
1798         row_type const actrow = tabular.row_of_cell(cur.idx());
1799         for (row_type r1 = 0, r2 = actrow;
1800              r1 < paste_tabular->rows() && r2 < tabular.rows();
1801              ++r1, ++r2) {
1802                 for (col_type c1 = 0, c2 = actcol;
1803                     c1 < paste_tabular->columns() && c2 < tabular.columns();
1804                     ++c1, ++c2) {
1805                         if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
1806                             tabular.isPartOfMultiColumn(r2, c2))
1807                                 continue;
1808                         if (paste_tabular->isPartOfMultiColumn(r1, c1)) {
1809                                 --c2;
1810                                 continue;
1811                         }
1812                         if (tabular.isPartOfMultiColumn(r2, c2)) {
1813                                 --c1;
1814                                 continue;
1815                         }
1816                         shared_ptr<InsetText> inset(
1817                                 new InsetText(*paste_tabular->getCellInset(r1, c1)));
1818                         tabular.setCellInset(r2, c2, inset);
1819                         inset->markNew();
1820                         cur.pos() = 0;
1821                 }
1822         }
1823         return true;
1824 }
1825
1826
1827 void InsetTabular::cutSelection(LCursor & cur)
1828 {
1829         if (!cur.selection())
1830                 return;
1831
1832         row_type rs, re;
1833         col_type cs, ce;
1834         getSelection(cur, rs, re, cs, ce);
1835         for (row_type i = rs; i <= re; ++i) {
1836                 for (col_type j = cs; j <= ce; ++j) {
1837                         shared_ptr<InsetText> t
1838                                 = cell(tabular.getCellNumber(i, j));
1839                         if (cur.buffer().params().tracking_changes)
1840                                 t->markErased(true);
1841                         else
1842                                 t->clear();
1843                 }
1844         }
1845
1846         // cursor position might be invalid now
1847         cur.pos() = cur.lastpos();
1848         cur.clearSelection();
1849 }
1850
1851
1852 bool InsetTabular::isRightToLeft(LCursor & cur) const
1853 {
1854         BOOST_ASSERT(cur.depth() > 1);
1855         Paragraph const & parentpar = cur[cur.depth() - 2].paragraph();
1856         LCursor::pos_type const parentpos = cur[cur.depth() - 2].pos();
1857         return parentpar.getFontSettings(cur.bv().buffer()->params(),
1858                                          parentpos).language()->rightToLeft();
1859 }
1860
1861
1862 void InsetTabular::getSelection(LCursor & cur,
1863         row_type & rs, row_type & re, col_type & cs, col_type & ce) const
1864 {
1865         CursorSlice const & beg = cur.selBegin();
1866         CursorSlice const & end = cur.selEnd();
1867         cs = tabular.column_of_cell(beg.idx());
1868         ce = tabular.column_of_cell(end.idx());
1869         if (cs > ce) {
1870                 ce = cs;
1871                 cs = tabular.column_of_cell(end.idx());
1872         } else {
1873                 ce = tabular.right_column_of_cell(end.idx());
1874         }
1875
1876         rs = tabular.row_of_cell(beg.idx());
1877         re = tabular.row_of_cell(end.idx());
1878         if (rs > re)
1879                 swap(rs, re);
1880 }
1881
1882
1883 LyXText * InsetTabular::getText(int idx) const
1884 {
1885         return size_t(idx) < nargs() ? cell(idx)->getText(0) : 0;
1886 }
1887
1888
1889 void InsetTabular::markErased(bool erased)
1890 {
1891         for (idx_type idx = 0; idx < nargs(); ++idx)
1892                 cell(idx)->markErased(erased);
1893 }
1894
1895
1896 bool InsetTabular::forceDefaultParagraphs(idx_type cell) const
1897 {
1898         return tabular.getPWidth(cell).zero();
1899 }
1900
1901
1902 bool InsetTabular::insertAsciiString(BufferView & bv, string const & buf,
1903                                      bool usePaste)
1904 {
1905         if (buf.length() <= 0)
1906                 return true;
1907
1908         col_type cols = 1;
1909         row_type rows = 1;
1910         col_type maxCols = 1;
1911         string::size_type const len = buf.length();
1912         string::size_type p = 0;
1913
1914         while (p < len && (p = buf.find_first_of("\t\n", p)) != string::npos) {
1915                 switch (buf[p]) {
1916                 case '\t':
1917                         ++cols;
1918                         break;
1919                 case '\n':
1920                         if (p + 1 < len)
1921                                 ++rows;
1922                         maxCols = max(cols, maxCols);
1923                         cols = 1;
1924                         break;
1925                 }
1926                 ++p;
1927         }
1928         maxCols = max(cols, maxCols);
1929         LyXTabular * loctab;
1930         idx_type cell = 0;
1931         col_type ocol = 0;
1932         row_type row = 0;
1933         if (usePaste) {
1934                 paste_tabular.reset(
1935                         new LyXTabular(bv.buffer()->params(), rows, maxCols));
1936                 loctab = paste_tabular.get();
1937                 cols = 0;
1938                 dirtyTabularStack(true);
1939         } else {
1940                 loctab = &tabular;
1941                 cell = bv.cursor().idx();
1942                 ocol = tabular.column_of_cell(cell);
1943                 row = tabular.row_of_cell(cell);
1944         }
1945
1946         string::size_type op = 0;
1947         idx_type const cells = loctab->getNumberOfCells();
1948         p = 0;
1949         cols = ocol;
1950         rows = loctab->rows();
1951         col_type const columns = loctab->columns();
1952
1953         while (cell < cells && p < len && row < rows &&
1954                (p = buf.find_first_of("\t\n", p)) != string::npos)
1955         {
1956                 if (p >= len)
1957                         break;
1958                 switch (buf[p]) {
1959                 case '\t':
1960                         // we can only set this if we are not too far right
1961                         if (cols < columns) {
1962                                 shared_ptr<InsetText> inset = loctab->getCellInset(cell);
1963                                 inset->setViewCache(&bv);
1964                                 Paragraph & par = inset->text_.getPar(0);
1965                                 LyXFont const font = inset->text_.getFont(par, 0);
1966                                 inset->setText(buf.substr(op, p - op), font);
1967                                 ++cols;
1968                                 ++cell;
1969                         }
1970                         break;
1971                 case '\n':
1972                         // we can only set this if we are not too far right
1973                         if (cols < columns) {
1974                                 shared_ptr<InsetText> inset = tabular.getCellInset(cell);
1975                                 inset->setViewCache(&bv);
1976                                 Paragraph & par = inset->text_.getPar(0);
1977                                 LyXFont const font = inset->text_.getFont(par, 0);
1978                                 inset->setText(buf.substr(op, p - op), font);
1979                         }
1980                         cols = ocol;
1981                         ++row;
1982                         if (row < rows)
1983                                 cell = loctab->getCellNumber(row, cols);
1984                         break;
1985                 }
1986                 ++p;
1987                 op = p;
1988         }
1989         // check for the last cell if there is no trailing '\n'
1990         if (cell < cells && op < len) {
1991                 shared_ptr<InsetText> inset = loctab->getCellInset(cell);
1992                 inset->setViewCache(&bv);
1993                 Paragraph & par = inset->text_.getPar(0);
1994                 LyXFont const font = inset->text_.getFont(par, 0);
1995                 inset->setText(buf.substr(op, len - op), font);
1996         }
1997         return true;
1998 }
1999
2000
2001 void InsetTabular::addPreview(PreviewLoader & loader) const
2002 {
2003         row_type const rows = tabular.rows();
2004         col_type const columns = tabular.columns();
2005         for (row_type i = 0; i < rows; ++i) {
2006                 for (col_type j = 0; j < columns; ++j)
2007                         tabular.getCellInset(i, j)->addPreview(loader);
2008         }
2009 }
2010
2011
2012 bool InsetTabular::tablemode(LCursor & cur) const
2013 {
2014         return cur.selection() && cur.selBegin().idx() != cur.selEnd().idx();
2015 }
2016
2017
2018
2019
2020
2021 string const InsetTabularMailer::name_("tabular");
2022
2023 InsetTabularMailer::InsetTabularMailer(InsetTabular const & inset)
2024         : inset_(const_cast<InsetTabular &>(inset))
2025 {}
2026
2027
2028 string const InsetTabularMailer::inset2string(Buffer const &) const
2029 {
2030         return params2string(inset_);
2031 }
2032
2033
2034 void InsetTabularMailer::string2params(string const & in, InsetTabular & inset)
2035 {
2036         istringstream data(in);
2037         LyXLex lex(0,0);
2038         lex.setStream(data);
2039
2040         if (in.empty())
2041                 return;
2042
2043         string token;
2044         lex >> token;
2045         if (!lex || token != name_)
2046                 return print_mailer_error("InsetTabularMailer", in, 1,
2047                                           name_);
2048
2049         // This is part of the inset proper that is usually swallowed
2050         // by Buffer::readInset
2051         lex >> token;
2052         if (!lex || token != "Tabular")
2053                 return print_mailer_error("InsetTabularMailer", in, 2,
2054                                           "Tabular");
2055
2056         Buffer const & buffer = inset.buffer();
2057         inset.read(buffer, lex);
2058 }
2059
2060
2061 string const InsetTabularMailer::params2string(InsetTabular const & inset)
2062 {
2063         ostringstream data;
2064         data << name_ << ' ';
2065         inset.write(inset.buffer(), data);
2066         data << "\\end_inset\n";
2067         return data.str();
2068 }