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