]> git.lyx.org Git - features.git/blob - src/insets/insettabular.C
no need for three calls to update() in a row...
[features.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 "debug.h"
20 #include "dispatchresult.h"
21 #include "funcrequest.h"
22 #include "FuncStatus.h"
23 #include "gettext.h"
24 #include "language.h"
25 #include "LColor.h"
26 #include "lyx_cb.h"
27 #include "lyxlex.h"
28 #include "metricsinfo.h"
29 #include "outputparams.h"
30 #include "paragraph.h"
31 #include "paragraph_funcs.h"
32 #include "ParagraphParameters.h"
33 #include "undo.h"
34
35 #include "frontends/Alert.h"
36 #include "frontends/font_metrics.h"
37 #include "frontends/LyXView.h"
38 #include "frontends/Painter.h"
39
40 #include "support/std_sstream.h"
41 #include <iostream>
42
43 using lyx::graphics::PreviewLoader;
44
45 using lyx::support::ltrim;
46 using lyx::support::strToInt;
47 using lyx::support::strToDbl;
48
49 using std::endl;
50 using std::max;
51 using std::swap;
52 using std::string;
53 using std::auto_ptr;
54 using std::istringstream;
55 using std::ostream;
56 using std::ostringstream;
57
58
59 namespace {
60
61 int const ADD_TO_HEIGHT = 2;
62 int const ADD_TO_TABULAR_WIDTH = 2;
63
64 ///
65 boost::scoped_ptr<LyXTabular> paste_tabular;
66
67
68 struct TabularFeature {
69         LyXTabular::Feature action;
70         string feature;
71 };
72
73
74 TabularFeature tabularFeature[] =
75 {
76         { LyXTabular::APPEND_ROW, "append-row" },
77         { LyXTabular::APPEND_COLUMN, "append-column" },
78         { LyXTabular::DELETE_ROW, "delete-row" },
79         { LyXTabular::DELETE_COLUMN, "delete-column" },
80         { LyXTabular::TOGGLE_LINE_TOP, "toggle-line-top" },
81         { LyXTabular::TOGGLE_LINE_BOTTOM, "toggle-line-bottom" },
82         { LyXTabular::TOGGLE_LINE_LEFT, "toggle-line-left" },
83         { LyXTabular::TOGGLE_LINE_RIGHT, "toggle-line-right" },
84         { LyXTabular::ALIGN_LEFT, "align-left" },
85         { LyXTabular::ALIGN_RIGHT, "align-right" },
86         { LyXTabular::ALIGN_CENTER, "align-center" },
87         { LyXTabular::ALIGN_BLOCK, "align-block" },
88         { LyXTabular::VALIGN_TOP, "valign-top" },
89         { LyXTabular::VALIGN_BOTTOM, "valign-bottom" },
90         { LyXTabular::VALIGN_MIDDLE, "valign-middle" },
91         { LyXTabular::M_TOGGLE_LINE_TOP, "m-toggle-line-top" },
92         { LyXTabular::M_TOGGLE_LINE_BOTTOM, "m-toggle-line-bottom" },
93         { LyXTabular::M_TOGGLE_LINE_LEFT, "m-toggle-line-left" },
94         { LyXTabular::M_TOGGLE_LINE_RIGHT, "m-toggle-line-right" },
95         { LyXTabular::M_ALIGN_LEFT, "m-align-left" },
96         { LyXTabular::M_ALIGN_RIGHT, "m-align-right" },
97         { LyXTabular::M_ALIGN_CENTER, "m-align-center" },
98         { LyXTabular::M_VALIGN_TOP, "m-valign-top" },
99         { LyXTabular::M_VALIGN_BOTTOM, "m-valign-bottom" },
100         { LyXTabular::M_VALIGN_MIDDLE, "m-valign-middle" },
101         { LyXTabular::MULTICOLUMN, "multicolumn" },
102         { LyXTabular::SET_ALL_LINES, "set-all-lines" },
103         { LyXTabular::UNSET_ALL_LINES, "unset-all-lines" },
104         { LyXTabular::SET_LONGTABULAR, "set-longtabular" },
105         { LyXTabular::UNSET_LONGTABULAR, "unset-longtabular" },
106         { LyXTabular::SET_PWIDTH, "set-pwidth" },
107         { LyXTabular::SET_MPWIDTH, "set-mpwidth" },
108         { LyXTabular::SET_ROTATE_TABULAR, "set-rotate-tabular" },
109         { LyXTabular::UNSET_ROTATE_TABULAR, "unset-rotate-tabular" },
110         { LyXTabular::SET_ROTATE_CELL, "set-rotate-cell" },
111         { LyXTabular::UNSET_ROTATE_CELL, "unset-rotate-cell" },
112         { LyXTabular::SET_USEBOX, "set-usebox" },
113         { LyXTabular::SET_LTHEAD, "set-lthead" },
114         { LyXTabular::SET_LTFIRSTHEAD, "set-ltfirsthead" },
115         { LyXTabular::SET_LTFOOT, "set-ltfoot" },
116         { LyXTabular::SET_LTLASTFOOT, "set-ltlastfoot" },
117         { LyXTabular::SET_LTNEWPAGE, "set-ltnewpage" },
118         { LyXTabular::SET_SPECIAL_COLUMN, "set-special-column" },
119         { LyXTabular::SET_SPECIAL_MULTI, "set-special-multi" },
120         { LyXTabular::LAST_ACTION, "" }
121 };
122
123 struct FindFeature {
124         ///
125         FindFeature(LyXTabular::Feature feature)
126                 : feature_(feature)
127         {}
128         ///
129         bool operator()(TabularFeature & tf)
130         {
131                 return tf.action == feature_;
132         }
133 private:
134         ///
135         LyXTabular::Feature feature_;
136 };
137
138 } // namespace anon
139
140
141 string const featureAsString(LyXTabular::Feature feature)
142 {
143         TabularFeature * it  = tabularFeature;
144         TabularFeature * end = it +
145                 sizeof(tabularFeature) / sizeof(TabularFeature);
146         it = std::find_if(it, end, FindFeature(feature));
147         return (it == end) ? string() : it->feature;
148 }
149
150
151 bool InsetTabular::hasPasteBuffer() const
152 {
153         return (paste_tabular.get() != 0);
154 }
155
156
157 InsetTabular::InsetTabular(Buffer const & buf, int rows, int columns)
158         : tabular(buf.params(), max(rows, 1), max(columns, 1)),
159           buffer_(&buf), cursorx_(0), cursory_(0)
160 {
161         tabular.setOwner(this);
162         the_locking_inset = 0;
163         actrow = 0;
164         actcell = 0;
165         clearSelection();
166         in_reset_pos = 0;
167 }
168
169
170 InsetTabular::InsetTabular(InsetTabular const & tab)
171         : UpdatableInset(tab), tabular(tab.tabular),
172                 buffer_(tab.buffer_), cursorx_(0), cursory_(0)
173 {
174         tabular.setOwner(this);
175         the_locking_inset = 0;
176         actrow = 0;
177         actcell = 0;
178         clearSelection();
179         in_reset_pos = 0;
180 }
181
182
183 InsetTabular::~InsetTabular()
184 {
185         InsetTabularMailer(*this).hideDialog();
186 }
187
188
189 auto_ptr<InsetBase> InsetTabular::clone() const
190 {
191         return auto_ptr<InsetBase>(new InsetTabular(*this));
192 }
193
194
195 Buffer const & InsetTabular::buffer() const
196 {
197         return *buffer_;
198 }
199
200
201 void InsetTabular::buffer(Buffer * b)
202 {
203         buffer_ = b;
204 }
205
206
207 void InsetTabular::write(Buffer const & buf, ostream & os) const
208 {
209         os << "Tabular" << endl;
210         tabular.write(buf, os);
211 }
212
213
214 void InsetTabular::read(Buffer const & buf, LyXLex & lex)
215 {
216         bool const old_format = (lex.getString() == "\\LyXTable");
217
218         tabular.read(buf, lex);
219
220         if (old_format)
221                 return;
222
223         lex.nextToken();
224         string token = lex.getString();
225         while (lex.isOK() && (token != "\\end_inset")) {
226                 lex.nextToken();
227                 token = lex.getString();
228         }
229         if (token != "\\end_inset") {
230                 lex.printError("Missing \\end_inset at this point. "
231                                "Read: `$$Token'");
232         }
233 }
234
235
236 void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
237 {
238         //lyxerr << "InsetTabular::metrics: " << mi.base.bv << " width: " <<
239         //      mi.base.textwidth << "\n";
240         if (!mi.base.bv) {
241                 lyxerr << "InsetTabular::metrics: need bv" << endl;
242                 BOOST_ASSERT(false);
243         }
244
245         calculate_dimensions_of_cells(mi);
246
247         dim.asc = tabular.getAscentOfRow(0);
248         dim.des = tabular.getHeightOfTabular() - tabular.getAscentOfRow(0) + 1;
249         dim.wid = tabular.getWidthOfTabular() + 2 * ADD_TO_TABULAR_WIDTH;
250         dim_ = dim;
251 }
252
253
254 void InsetTabular::draw(PainterInfo & pi, int x, int y) const
255 {
256         //lyxerr << "InsetTabular::draw: " << x << " " << y << endl;
257
258         BufferView * bv = pi.base.bv;
259
260         if (!owner())
261                 x += scroll();
262
263         xo_ = x;
264         yo_ = y;
265         x += ADD_TO_TABULAR_WIDTH;
266
267         int cell = 0;
268         first_visible_cell = -1;
269         for (int i = 0; i < tabular.rows(); ++i) {
270                 int nx = x;
271                 cell = tabular.getCellNumber(i, 0);
272                 if (y + tabular.getDescentOfRow(i) <= 0 &&
273                           y - tabular.getAscentOfRow(i) < pi.pain.paperHeight())
274                 {
275                         y += tabular.getDescentOfRow(i) +
276                                         tabular.getAscentOfRow(i + 1) +
277                                         tabular.getAdditionalHeight(i + 1);
278                         continue;
279                 }
280                 for (int j = 0; j < tabular.columns(); ++j) {
281                         if (nx > bv->workWidth())
282                                 break;
283                         if (tabular.isPartOfMultiColumn(i, j))
284                                 continue;
285                         int cx = nx + tabular.getBeginningOfTextInCell(cell);
286                         if (first_visible_cell < 0)
287                                 first_visible_cell = cell;
288                         if (hasSelection()) {
289                                 drawCellSelection(pi.pain, nx, y, i, j, cell);
290                         }
291
292                         tabular.getCellInset(cell).draw(pi, cx, y);
293                         drawCellLines(pi.pain, nx, y, i, cell);
294                         nx += tabular.getWidthOfColumn(cell);
295                         ++cell;
296                 }
297
298 // Would be nice, but for some completely unfathomable reason,
299 // on a col resize to a new fixed width, even though the insettexts
300 // are resized, the cell isn't, but drawing all cells in a tall table
301 // has the desired effect somehow. Complete dark magic.
302 #if 0
303                 // avoiding drawing the rest of a long table is
304                 // a pretty big speedup
305                 if (y > bv->workHeight())
306                         break;
307 #endif
308
309                 y += tabular.getDescentOfRow(i) +
310                         tabular.getAscentOfRow(i + 1) +
311                         tabular.getAdditionalHeight(i + 1);
312         }
313 }
314
315
316 void InsetTabular::drawCellLines(Painter & pain, int x, int y,
317                                  int row, int cell) const
318 {
319         int x2 = x + tabular.getWidthOfColumn(cell);
320         bool on_off = false;
321
322         if (!tabular.topAlreadyDrawn(cell)) {
323                 on_off = !tabular.topLine(cell);
324                 pain.line(x, y - tabular.getAscentOfRow(row),
325                           x2, y -  tabular.getAscentOfRow(row),
326                           on_off ? LColor::tabularonoffline : LColor::tabularline,
327                           on_off ? Painter::line_onoffdash : Painter::line_solid);
328         }
329         on_off = !tabular.bottomLine(cell);
330         pain.line(x, y + tabular.getDescentOfRow(row),
331                   x2, y + tabular.getDescentOfRow(row),
332                   on_off ? LColor::tabularonoffline : LColor::tabularline,
333                   on_off ? Painter::line_onoffdash : Painter::line_solid);
334         if (!tabular.leftAlreadyDrawn(cell)) {
335                 on_off = !tabular.leftLine(cell);
336                 pain.line(x, y -  tabular.getAscentOfRow(row),
337                           x, y +  tabular.getDescentOfRow(row),
338                           on_off ? LColor::tabularonoffline : LColor::tabularline,
339                           on_off ? Painter::line_onoffdash : Painter::line_solid);
340         }
341         on_off = !tabular.rightLine(cell);
342         pain.line(x2 - tabular.getAdditionalWidth(cell),
343                   y -  tabular.getAscentOfRow(row),
344                   x2 - tabular.getAdditionalWidth(cell),
345                   y +  tabular.getDescentOfRow(row),
346                   on_off ? LColor::tabularonoffline : LColor::tabularline,
347                   on_off ? Painter::line_onoffdash : Painter::line_solid);
348 }
349
350
351 void InsetTabular::drawCellSelection(Painter & pain, int x, int y,
352                                      int row, int column, int cell) const
353 {
354         BOOST_ASSERT(hasSelection());
355         int cs = tabular.column_of_cell(sel_cell_start);
356         int ce = tabular.column_of_cell(sel_cell_end);
357         if (cs > ce) {
358                 ce = cs;
359                 cs = tabular.column_of_cell(sel_cell_end);
360         } else {
361                 ce = tabular.right_column_of_cell(sel_cell_end);
362         }
363
364         int rs = tabular.row_of_cell(sel_cell_start);
365         int re = tabular.row_of_cell(sel_cell_end);
366         if (rs > re)
367                 swap(rs, re);
368
369         if (column >= cs && column <= ce && row >= rs && row <= re) {
370                 int w = tabular.getWidthOfColumn(cell);
371                 int h = tabular.getAscentOfRow(row) + tabular.getDescentOfRow(row)-1;
372                 pain.fillRectangle(x, y - tabular.getAscentOfRow(row) + 1,
373                                    w, h, LColor::selection);
374         }
375 }
376
377
378 string const InsetTabular::editMessage() const
379 {
380         return _("Opened table");
381 }
382
383
384 void InsetTabular::updateLocal(BufferView * bv) const
385 {
386         bv->update();
387         resetPos(bv);
388 }
389
390
391 int InsetTabular::insetInInsetY() const
392 {
393         if (the_locking_inset)
394                 return cursory_ + the_locking_inset->insetInInsetY();
395         return 0;
396 }
397
398
399 bool InsetTabular::insertInset(BufferView * bv, InsetOld * inset)
400 {
401         return the_locking_inset && the_locking_inset->insertInset(bv, inset);
402 }
403
404
405 void InsetTabular::lfunMousePress(FuncRequest const & cmd)
406 {
407         if (hasSelection() && cmd.button() == mouse_button::button3)
408                 return;
409
410         if (hasSelection())
411                 clearSelection();
412
413         BufferView * bv = cmd.view();
414
415         the_locking_inset = 0;
416         setPos(bv, cmd.x, cmd.y);
417         clearSelection();
418         the_locking_inset = 0;
419
420         if (cmd.button() == mouse_button::button2)
421                 dispatch(FuncRequest(bv, LFUN_PASTESELECTION, "paragraph"));
422 }
423
424
425 void InsetTabular::lfunMouseRelease(FuncRequest const & cmd)
426 {
427         if (cmd.button() == mouse_button::button3)
428                 InsetTabularMailer(*this).showDialog(cmd.view());
429 }
430
431
432 void InsetTabular::lfunMouseMotion(FuncRequest const & cmd)
433 {
434         BufferView * bv = cmd.view();
435         int const old_cell = actcell;
436
437         setPos(bv, cmd.x, cmd.y);
438         if (!hasSelection())
439                 setSelection(actcell, actcell);
440         else if (old_cell != actcell)
441                 setSelection(sel_cell_start, actcell);
442 }
443
444
445 void InsetTabular::edit(BufferView * bv, bool left)
446 {
447         lyxerr << "InsetTabular::edit: " << this
448                 << " first text: " << tabular.cell_info[0][0].inset.getText(0) 
449                 << " first cell: " << &tabular.cell_info[0][0].inset << endl;
450
451         finishUndo();
452         the_locking_inset = 0;
453
454         if (left) {
455                 if (isRightToLeft(bv))
456                         actcell = tabular.getLastCellInRow(0);
457                 else
458                         actcell = 0;
459         } else {
460                 if (isRightToLeft(bv))
461                         actcell = tabular.getFirstCellInRow(tabular.rows()-1);
462                 else
463                         actcell = tabular.getNumberOfCells() - 1;
464         }
465         clearSelection();
466         resetPos(bv);
467         bv->fitCursor();
468         bv->cursor().push(this);
469 }
470
471
472 void InsetTabular::edit(BufferView * bv, int x, int y)
473 {
474         lyxerr << "InsetTabular::edit: " << this << " first cell "
475                 << &tabular.cell_info[0][0].inset << endl;
476
477         finishUndo();
478         the_locking_inset = 0;
479         setPos(bv, x, y);
480         clearSelection();
481         finishUndo();
482         int xx = cursorx_ - xo_ + tabular.getBeginningOfTextInCell(actcell);
483         bv->cursor().push(this);
484         if (x > xx)
485                 activateCellInset(bv, x - xx, y - cursory_);
486 }
487
488
489 DispatchResult
490 InsetTabular::priv_dispatch(FuncRequest const & cmd, idx_type &, pos_type &)
491 {
492         lyxerr << "InsetTabular::dispatch: " << cmd << endl;
493         // We need to save the value of the_locking_inset as the call to
494         // the_locking_inset->localDispatch might unlock it.
495         DispatchResult result(true, true);
496         BufferView * bv  = cmd.view();
497         bool hs = hasSelection();
498
499         switch (cmd.action) {
500
501         case LFUN_MOUSE_PRESS:
502                 lfunMousePress(cmd);
503                 return DispatchResult(true, true);
504
505         case LFUN_MOUSE_MOTION:
506                 lfunMouseMotion(cmd);
507                 return DispatchResult(true, true);
508
509         case LFUN_MOUSE_RELEASE:
510                 lfunMouseRelease(cmd);
511                 return DispatchResult(true, true);
512
513         case LFUN_CELL_BACKWARD:
514         case LFUN_CELL_FORWARD:
515                 if (cmd.action == LFUN_CELL_FORWARD)
516                         moveNextCell(bv, the_locking_inset != 0);
517                 else
518                         movePrevCell(bv, the_locking_inset != 0);
519                 clearSelection();
520                 if (!the_locking_inset)
521                         return DispatchResult(true);
522                 return result;
523
524         case LFUN_SCROLL_INSET:
525                 if (!cmd.argument.empty()) {
526                         if (cmd.argument.find('.') != cmd.argument.npos)
527                                 scroll(cmd.view(), static_cast<float>(strToDbl(cmd.argument)));
528                         else
529                                 scroll(cmd.view(), strToInt(cmd.argument));
530                         cmd.view()->update();
531                         return DispatchResult(true, true);
532                 }
533
534         case LFUN_RIGHTSEL: {
535                 int const start = hasSelection() ? sel_cell_start : actcell;
536                 if (tabular.isLastCellInRow(actcell)) {
537                         setSelection(start, actcell);
538                         break;
539                 }
540
541                 int end = actcell;
542                 // if we are starting a selection, only select
543                 // the current cell at the beginning
544                 if (hasSelection()) {
545                         moveRight(bv, false);
546                         end = actcell;
547                 }
548                 setSelection(start, end);
549                 break;
550         }
551
552         case LFUN_RIGHT:
553                 result = moveRight(bv, true);
554                 clearSelection();
555                 break;
556
557         case LFUN_LEFTSEL: {
558                 int const start = hasSelection() ? sel_cell_start : actcell;
559                 if (tabular.isFirstCellInRow(actcell)) {
560                         setSelection(start, actcell);
561                         break;
562                 }
563
564                 int end = actcell;
565                 // if we are starting a selection, only select
566                 // the current cell at the beginning
567                 if (hasSelection()) {
568                         moveLeft(bv, false);
569                         end = actcell;
570                 }
571                 setSelection(start, end);
572                 break;
573         }
574
575         case LFUN_LEFT:
576                 result = moveLeft(bv, true);
577                 clearSelection();
578                 break;
579
580         case LFUN_DOWNSEL: {
581                 int const start = hasSelection() ? sel_cell_start : actcell;
582                 int const ocell = actcell;
583                 // if we are starting a selection, only select
584                 // the current cell at the beginning
585                 if (hasSelection()) {
586                         moveDown(bv, false);
587                         if (ocell == sel_cell_end ||
588                             tabular.column_of_cell(ocell) > tabular.column_of_cell(actcell))
589                                 setSelection(start, tabular.getCellBelow(sel_cell_end));
590                         else
591                                 setSelection(start, tabular.getLastCellBelow(sel_cell_end));
592                 } else {
593                         setSelection(start, start);
594                 }
595                 break;
596         }
597
598         case LFUN_DOWN:
599                 result = moveDown(bv, the_locking_inset != 0);
600                 clearSelection();
601                 break;
602
603         case LFUN_UPSEL: {
604                 int const start = hasSelection() ? sel_cell_start : actcell;
605                 int const ocell = actcell;
606                 // if we are starting a selection, only select
607                 // the current cell at the beginning
608                 if (hasSelection()) {
609                         moveUp(bv, false);
610                         if (ocell == sel_cell_end ||
611                             tabular.column_of_cell(ocell) > tabular.column_of_cell(actcell))
612                                 setSelection(start, tabular.getCellAbove(sel_cell_end));
613                         else
614                                 setSelection(start, tabular.getLastCellAbove(sel_cell_end));
615                 } else {
616                         setSelection(start, start);
617                 }
618                 break;
619         }
620
621         case LFUN_UP:
622                 result = moveUp(bv, the_locking_inset != 0);
623                 clearSelection();
624                 break;
625
626         case LFUN_NEXT: {
627                 if (hs)
628                         clearSelection();
629                 int column = actcol;
630                 if (bv->top_y() + bv->painter().paperHeight()
631                     < yo_ + tabular.getHeightOfTabular())
632                 {
633                         bv->scrollDocView(bv->top_y() + bv->painter().paperHeight());
634                         actcell = tabular.getCellBelow(first_visible_cell) + column;
635                 } else {
636                         actcell = tabular.getFirstCellInRow(tabular.rows() - 1) + column;
637                 }
638                 resetPos(bv);
639                 break;
640         }
641
642         case LFUN_PRIOR: {
643                 if (hs)
644                         clearSelection();
645                 int column = actcol;
646                 if (yo_ < 0) {
647                         bv->scrollDocView(bv->top_y() - bv->painter().paperHeight());
648                         if (yo_ > 0)
649                                 actcell = column;
650                         else
651                                 actcell = tabular.getCellBelow(first_visible_cell) + column;
652                 } else {
653                         actcell = column;
654                 }
655                 resetPos(bv);
656                 break;
657         }
658
659         // none of these make sense for insettabular,
660         // but we must catch them to prevent any
661         // selection from being confused
662         case LFUN_PRIORSEL:
663         case LFUN_NEXTSEL:
664         case LFUN_WORDLEFT:
665         case LFUN_WORDLEFTSEL:
666         case LFUN_WORDRIGHT:
667         case LFUN_WORDRIGHTSEL:
668         case LFUN_WORDSEL:
669         case LFUN_DOWN_PARAGRAPH:
670         case LFUN_DOWN_PARAGRAPHSEL:
671         case LFUN_UP_PARAGRAPH:
672         case LFUN_UP_PARAGRAPHSEL:
673         case LFUN_BACKSPACE:
674         case LFUN_HOME:
675         case LFUN_HOMESEL:
676         case LFUN_END:
677         case LFUN_ENDSEL:
678         case LFUN_BEGINNINGBUF:
679         case LFUN_BEGINNINGBUFSEL:
680         case LFUN_ENDBUF:
681         case LFUN_ENDBUFSEL:
682                 break;
683
684         case LFUN_LAYOUT_TABULAR:
685                 InsetTabularMailer(*this).showDialog(bv);
686                 break;
687
688         case LFUN_INSET_DIALOG_UPDATE:
689                 InsetTabularMailer(*this).updateDialog(bv);
690                 break;
691
692         case LFUN_TABULAR_FEATURE:
693                 if (!tabularFeatures(bv, cmd.argument))
694                         result = DispatchResult(false);
695                 break;
696
697         // insert file functions
698         case LFUN_FILE_INSERT_ASCII_PARA:
699         case LFUN_FILE_INSERT_ASCII: {
700                 string tmpstr = getContentsOfAsciiFile(bv, cmd.argument, false);
701                 if (!tmpstr.empty() && !insertAsciiString(bv, tmpstr, false))
702                         result = DispatchResult(false);
703                 break;
704         }
705
706         case LFUN_LANGUAGE:
707         case LFUN_EMPH:
708         case LFUN_BOLD:
709         case LFUN_NOUN:
710         case LFUN_CODE:
711         case LFUN_SANS:
712         case LFUN_ROMAN:
713         case LFUN_DEFAULT:
714         case LFUN_UNDERLINE:
715         case LFUN_FONT_SIZE:
716                 lyxerr << "font changes not re-implemented for tables after LOCK" << endl;
717                 break;
718
719         case LFUN_FINISHED_LEFT:
720                 lyxerr << "swallow LFUN_FINISHED_LEFT, act: " << actcell << endl;
721                 if (!movePrevCell(bv, false))
722                         result = DispatchResult(FINISHED);
723                 break;
724
725         case LFUN_FINISHED_RIGHT:
726                 lyxerr << "swallow LFUN_FINISHED_RIGHT, act: " << actcell << endl;
727                 if (!moveNextCell(bv, false))
728                         result = DispatchResult(FINISHED_RIGHT);
729                 break;
730
731         case LFUN_FINISHED_UP:
732                 lyxerr << "swallow LFUN_FINISHED_UP, act: " << actcell << endl;
733                 result = moveUp(bv, true);
734                 break;
735
736         case LFUN_FINISHED_DOWN:
737                 lyxerr << "swallow LFUN_FINISHED_DOWN, act: " << actcell << endl;
738                 result = moveDown(bv, true);
739                 break;
740
741         case LFUN_CUT:
742                 if (copySelection(bv)) {
743                         recordUndo(bv, Undo::DELETE);
744                         cutSelection(bv->buffer()->params());
745                 }
746                 break;
747
748         case LFUN_DELETE:
749                 recordUndo(bv, Undo::DELETE);
750                 cutSelection(bv->buffer()->params());
751                 break;
752
753         case LFUN_COPY:
754                 if (!hasSelection())
755                         break;
756                 finishUndo();
757                 copySelection(bv);
758                 break;
759
760         case LFUN_PASTESELECTION: {
761                 string const clip = bv->getClipboard();
762                 if (clip.empty())
763                         break;
764 #if 0
765                 if (clip.find('\t') != string::npos) {
766                         int cols = 1;
767                         int rows = 1;
768                         int maxCols = 1;
769                         string::size_type len = clip.length();
770                         string::size_type p = 0;
771
772                         while (p < len &&
773                               ((p = clip.find_first_of("\t\n", p)) != string::npos)) {
774                                 switch (clip[p]) {
775                                 case '\t':
776                                         ++cols;
777                                         break;
778                                 case '\n':
779                                         if ((p+1) < len)
780                                                 ++rows;
781                                         maxCols = max(cols, maxCols);
782                                         cols = 1;
783                                         break;
784                                 }
785                                 ++p;
786                         }
787                         maxCols = max(cols, maxCols);
788
789                         paste_tabular.reset(
790                                 new LyXTabular(bv->buffer()->params(),
791                                                this, rows, maxCols)
792                                 );
793
794                         string::size_type op = 0;
795                         int cell = 0;
796                         int cells = paste_tabular->getNumberOfCells();
797                         p = 0;
798                         cols = 0;
799                         while (cell < cells && p < len &&
800                               (p = clip.find_first_of("\t\n", p)) != string::npos) {
801                                 if (p >= len)
802                                         break;
803                                 switch (clip[p]) {
804                                 case '\t':
805                                         paste_tabular->getCellInset(cell)->setText(clip.substr(op, p-op));
806                                         ++cols;
807                                         ++cell;
808                                         break;
809                                 case '\n':
810                                         paste_tabular->getCellInset(cell)->setText(clip.substr(op, p-op));
811                                         while (cols++ < maxCols)
812                                                 ++cell;
813                                         cols = 0;
814                                         break;
815                                 }
816                                 ++p;
817                                 op = p;
818                         }
819                         // check for the last cell if there is no trailing '\n'
820                         if (cell < cells && op < len)
821                                 paste_tabular->getCellInset(cell)->setText(clip.substr(op, len-op));
822                 } else
823 #else
824                 if (!insertAsciiString(bv, clip, true))
825 #endif
826                 {
827                         // so that the clipboard is used and it goes on
828                         // to default
829                         // and executes LFUN_PASTESELECTION in insettext!
830                         paste_tabular.reset();
831                 }
832                 // fall through
833         }
834
835         case LFUN_PASTE:
836                 if (hasPasteBuffer()) {
837                         recordUndo(bv, Undo::INSERT);
838                         pasteSelection(bv);
839                         break;
840                 }
841                 // fall through
842
843         // ATTENTION: the function above has to be PASTE and PASTESELECTION!!!
844
845         default:
846                 // handle font changing stuff on selection before we lock the inset
847                 // in the default part!
848                 result = DispatchResult(false);
849                 // we try to activate the actual inset and put this event down to
850                 // the insets dispatch function.
851                 if (!the_locking_inset && activateCellInset(bv)) {
852                         result = the_locking_inset->dispatch(cmd);
853                         if (!result.dispatched()) {
854                                 // we need to update if this was requested before
855                                 result = DispatchResult(false);
856                                 break;
857                         }
858                         if (hs)
859                                 clearSelection();
860                 }
861                 break;
862         }
863
864         updateLocal(bv);
865         InsetTabularMailer(*this).updateDialog(bv);
866         return result;
867 }
868
869
870 int InsetTabular::latex(Buffer const & buf, ostream & os,
871                         OutputParams const & runparams) const
872 {
873         return tabular.latex(buf, os, runparams);
874 }
875
876
877 int InsetTabular::plaintext(Buffer const & buf, ostream & os,
878                         OutputParams const & runparams) const
879 {
880         int dp = runparams.linelen ? ownerPar(buf, this).params().depth() : 0;
881         return tabular.plaintext(buf, os, runparams, dp, false, 0);
882 }
883
884
885 int InsetTabular::linuxdoc(Buffer const & buf, ostream & os,
886                            OutputParams const & runparams) const
887 {
888         return tabular.linuxdoc(buf,os, runparams);
889 }
890
891
892 int InsetTabular::docbook(Buffer const & buf, ostream & os,
893                           OutputParams const & runparams) const
894 {
895         int ret = 0;
896         InsetOld * master;
897
898         // if the table is inside a float it doesn't need the informaltable
899         // wrapper. Search for it.
900         for (master = owner();
901              master && master->lyxCode() != InsetOld::FLOAT_CODE;
902              master = master->owner());
903
904         if (!master) {
905                 os << "<informaltable>";
906                 if (runparams.mixed_content)
907                         os << endl;
908                 ++ret;
909         }
910         ret += tabular.docbook(buf, os, runparams);
911         if (!master) {
912                 os << "</informaltable>";
913                 if (runparams.mixed_content)
914                         os << endl;
915                 ++ret;
916         }
917         return ret;
918 }
919
920
921 void InsetTabular::validate(LaTeXFeatures & features) const
922 {
923         tabular.validate(features);
924 }
925
926
927 void InsetTabular::calculate_dimensions_of_cells(MetricsInfo & mi) const
928 {
929 #if 1
930         // if we have a locking_inset we should have to check only this cell for
931         // change so I'll try this to have a boost, but who knows ;) (Jug?)
932         // This is _really_ important (André)
933         if (the_locking_inset == &tabular.getCellInset(actcell)) {
934                 int maxAsc = 0;
935                 int maxDesc = 0;
936                 for (int j = 0; j < tabular.columns(); ++j) {
937                         Dimension dim;
938                         MetricsInfo m = mi;
939                         m.base.textwidth =
940                                 tabular.column_info[j].p_width.inPixels(mi.base.textwidth);
941                         tabular.getCellInset(actrow, j).metrics(m, dim);
942                         maxAsc  = max(dim.asc, maxAsc);
943                         maxDesc = max(dim.des, maxDesc);
944                 }
945                 tabular.setWidthOfCell(actcell, the_locking_inset->width());
946                 tabular.setAscentOfRow(actrow, maxAsc + ADD_TO_HEIGHT);
947                 tabular.setDescentOfRow(actrow, maxDesc + ADD_TO_HEIGHT);
948                 return;
949         }
950 #endif
951
952         int cell = -1;
953         for (int i = 0; i < tabular.rows(); ++i) {
954                 int maxAsc = 0;
955                 int maxDesc = 0;
956                 for (int j = 0; j < tabular.columns(); ++j) {
957                         if (tabular.isPartOfMultiColumn(i, j))
958                                 continue;
959                         ++cell;
960                         Dimension dim;
961                         MetricsInfo m = mi;
962                         m.base.textwidth =
963                                 tabular.column_info[j].p_width.inPixels(mi.base.textwidth);
964                         tabular.getCellInset(cell).metrics(m, dim);
965                         maxAsc  = max(maxAsc, dim.asc);
966                         maxDesc = max(maxDesc, dim.des);
967                         tabular.setWidthOfCell(cell, dim.wid);
968                 }
969                 tabular.setAscentOfRow(i, maxAsc + ADD_TO_HEIGHT);
970                 tabular.setDescentOfRow(i, maxDesc + ADD_TO_HEIGHT);
971         }
972 }
973
974
975 void InsetTabular::getCursorPos(BufferView *, int & x, int & y) const
976 {
977         x = TEXT_TO_INSET_OFFSET + cursorx_ - xo_;
978         y = TEXT_TO_INSET_OFFSET + cursory_;
979 }
980
981
982 void InsetTabular::setPos(BufferView * bv, int x, int y) const
983 {
984         cursory_ = 0;
985         actcell = 0;
986         actrow = 0;
987         actcol = 0;
988         int ly = tabular.getDescentOfRow(actrow);
989
990         // first search the right row
991         while (ly < y && actrow + 1 < tabular.rows()) {
992                 cursory_ += tabular.getDescentOfRow(actrow) +
993                                  tabular.getAscentOfRow(actrow + 1) +
994                                  tabular.getAdditionalHeight(actrow + 1);
995                 ++actrow;
996                 ly = cursory_ + tabular.getDescentOfRow(actrow);
997         }
998         actcell = tabular.getCellNumber(actrow, actcol);
999
1000         // now search the right column
1001         int lx = tabular.getWidthOfColumn(actcell) -
1002                 tabular.getAdditionalWidth(actcell);
1003
1004         for (; !tabular.isLastCellInRow(actcell) && lx < x; ++actcell)
1005                 lx += tabular.getWidthOfColumn(actcell + 1)
1006                         + tabular.getAdditionalWidth(actcell);
1007
1008         cursorx_ = lx - tabular.getWidthOfColumn(actcell) + xo_ + 2;
1009         resetPos(bv);
1010 }
1011
1012
1013 int InsetTabular::getCellXPos(int cell) const
1014 {
1015         int c = cell;
1016
1017         for (; !tabular.isFirstCellInRow(c); --c)
1018                 ;
1019         int lx = tabular.getWidthOfColumn(cell);
1020         for (; c < cell; ++c)
1021                 lx += tabular.getWidthOfColumn(c);
1022
1023         return lx - tabular.getWidthOfColumn(cell) + xo_;
1024 }
1025
1026
1027 void InsetTabular::resetPos(BufferView * bv) const
1028 {
1029 #ifdef WITH_WARNINGS
1030 #warning This should be fixed in the right manner (20011128 Jug)
1031 #endif
1032         // fast hack to fix infinite repaintings!
1033         if (in_reset_pos > 0)
1034                 return;
1035
1036         int cell = 0;
1037         actcol = tabular.column_of_cell(actcell);
1038         actrow = 0;
1039         cursory_ = 0;
1040         for (; cell < actcell && !tabular.isLastRow(cell); ++cell) {
1041                 if (tabular.isLastCellInRow(cell)) {
1042                         cursory_ += tabular.getDescentOfRow(actrow) +
1043                                          tabular.getAscentOfRow(actrow + 1) +
1044                                          tabular.getAdditionalHeight(actrow + 1);
1045                         ++actrow;
1046                 }
1047         }
1048
1049         // we need this only from here on!!!
1050         ++in_reset_pos;
1051         int const offset = ADD_TO_TABULAR_WIDTH + 2;
1052         int new_x = getCellXPos(actcell) + offset;
1053         int old_x = cursorx_;
1054         cursorx_ = new_x;
1055 //    cursor.x(getCellXPos(actcell) + offset);
1056         if (actcol < tabular.columns() - 1 && scroll(false) &&
1057                 tabular.getWidthOfTabular() < bv->workWidth()-20)
1058         {
1059                 scroll(bv, 0.0F);
1060                 updateLocal(bv);
1061         } else if (the_locking_inset &&
1062                  tabular.getWidthOfColumn(actcell) > bv->workWidth() - 20)
1063         {
1064                 int xx = cursorx_ - offset;
1065                 if (xx > bv->workWidth()-20) {
1066                         scroll(bv, - xx + bv->workWidth() - 60);
1067                         updateLocal(bv);
1068                 } else if (xx < 20) {
1069                         if (xx < 0)
1070                                 xx = -xx + 60;
1071                         else
1072                                 xx = 60;
1073                         scroll(bv, xx);
1074                         updateLocal(bv);
1075                 }
1076         } else if (cursorx_ - offset > 20 &&
1077                    cursorx_ - offset + tabular.getWidthOfColumn(actcell)
1078                    > bv->workWidth() - 20) {
1079                 scroll(bv, - tabular.getWidthOfColumn(actcell) - 20);
1080                 updateLocal(bv);
1081         } else if (cursorx_ - offset < 20) {
1082                 scroll(bv, 20 - cursorx_ + offset);
1083                 updateLocal(bv);
1084         } else if (scroll() && xo_ > 20 &&
1085                    xo_ + tabular.getWidthOfTabular() > bv->workWidth() - 20) {
1086                 scroll(bv, old_x - cursorx_);
1087                 updateLocal(bv);
1088         }
1089         InsetTabularMailer(*this).updateDialog(bv);
1090         in_reset_pos = 0;
1091 }
1092
1093
1094 DispatchResult InsetTabular::moveRight(BufferView * bv, bool lock)
1095 {
1096         if (lock) {
1097                 if (activateCellInset(bv))
1098                         return DispatchResult(true, true);
1099         } else {
1100                 bool moved = isRightToLeft(bv)
1101                         ? movePrevCell(bv) : moveNextCell(bv);
1102                 if (!moved)
1103                         return DispatchResult(false, FINISHED_RIGHT);
1104                 if (lock && activateCellInset(bv))
1105                         return DispatchResult(true, true);
1106         }
1107         resetPos(bv);
1108         return DispatchResult(true);
1109 }
1110
1111
1112 DispatchResult InsetTabular::moveLeft(BufferView * bv, bool lock)
1113 {
1114         bool moved = isRightToLeft(bv) ? moveNextCell(bv) : movePrevCell(bv);
1115         if (!moved)
1116                 return DispatchResult(false, FINISHED);
1117         // behind the inset
1118         if (lock && activateCellInset(bv, 0, 0, true))
1119                 return DispatchResult(true, true);
1120         resetPos(bv);
1121         return DispatchResult(true);
1122 }
1123
1124
1125 DispatchResult InsetTabular::moveUp(BufferView * bv, bool lock)
1126 {
1127         int const ocell = actcell;
1128         actcell = tabular.getCellAbove(actcell);
1129         if (actcell == ocell) { // we moved out of the inset
1130                 return DispatchResult(false, FINISHED_UP);
1131         }
1132         resetPos(bv);
1133         if (lock)
1134                 activateCellInset(bv, bv->x_target(), 0);
1135         return DispatchResult(true, true);
1136 }
1137
1138
1139 DispatchResult InsetTabular::moveDown(BufferView * bv, bool lock)
1140 {
1141         int const ocell = actcell;
1142         actcell = tabular.getCellBelow(actcell);
1143         if (actcell == ocell) { // we moved out of the inset
1144                 return DispatchResult(false, FINISHED_DOWN);
1145         }
1146         resetPos(bv);
1147         if (lock)
1148                 activateCellInset(bv, bv->x_target(), 0);
1149         return DispatchResult(true, true);
1150 }
1151
1152
1153 bool InsetTabular::moveNextCell(BufferView * bv, bool lock)
1154 {
1155         if (isRightToLeft(bv)) {
1156                 if (tabular.isFirstCellInRow(actcell)) {
1157                         int row = tabular.row_of_cell(actcell);
1158                         if (row == tabular.rows() - 1)
1159                                 return false;
1160                         actcell = tabular.getLastCellInRow(row);
1161                         actcell = tabular.getCellBelow(actcell);
1162                 } else {
1163                         if (!actcell)
1164                                 return false;
1165                         --actcell;
1166                 }
1167         } else {
1168                 if (tabular.isLastCell(actcell))
1169                         return false;
1170                 ++actcell;
1171         }
1172         if (lock) {
1173                 bool rtl = tabular.getCellInset(actcell).paragraphs.begin()->
1174                         isRightToLeftPar(bv->buffer()->params());
1175                 activateCellInset(bv, 0, 0, !rtl);
1176         }
1177         resetPos(bv);
1178         return true;
1179 }
1180
1181
1182 bool InsetTabular::movePrevCell(BufferView * bv, bool lock)
1183 {
1184         lyxerr << "move prevcell 1" << endl;
1185         if (isRightToLeft(bv)) {
1186                 lyxerr << "move prevcell a" << endl;
1187                 if (tabular.isLastCellInRow(actcell)) {
1188                         int row = tabular.row_of_cell(actcell);
1189                         if (row == 0)
1190                                 return false;
1191                         actcell = tabular.getFirstCellInRow(row);
1192                         actcell = tabular.getCellAbove(actcell);
1193                 } else {
1194                         if (tabular.isLastCell(actcell))
1195                                 return false;
1196                         ++actcell;
1197                 }
1198         } else {
1199                 lyxerr << "move prevcell b" << endl;
1200                 if (actcell == 0) // first cell
1201                         return false;
1202                 --actcell;
1203         }
1204         lyxerr << "move prevcell 2" << endl;
1205         if (lock) {
1206                 bool rtl = tabular.getCellInset(actcell).paragraphs.begin()->
1207                         isRightToLeftPar(bv->buffer()->params());
1208                 activateCellInset(bv, 0, 0, !rtl);
1209         }
1210         lyxerr << "move prevcell 3" << endl;
1211         resetPos(bv);
1212         lyxerr << "move prevcell 4" << endl;
1213         return true;
1214 }
1215
1216
1217 void InsetTabular::setFont(BufferView * bv, LyXFont const & font, bool tall,
1218                            bool selectall)
1219 {
1220         if (selectall) {
1221                 setSelection(0, tabular.getNumberOfCells() - 1);
1222         }
1223         if (hasSelection()) {
1224                 recordUndo(bv, Undo::ATOMIC);
1225                 bool const frozen = undo_frozen;
1226                 if (!frozen)
1227                         freezeUndo();
1228                 // apply the fontchange on the whole selection
1229                 int sel_row_start;
1230                 int sel_row_end;
1231                 int sel_col_start;
1232                 int sel_col_end;
1233                 getSelection(sel_row_start, sel_row_end, sel_col_start, sel_col_end);
1234                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1235                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1236                                 tabular.getCellInset(i, j).setFont(bv, font, tall, true);
1237
1238                 if (!frozen)
1239                         unFreezeUndo();
1240                 if (selectall)
1241                         clearSelection();
1242                 updateLocal(bv);
1243         }
1244         if (the_locking_inset)
1245                 the_locking_inset->setFont(bv, font, tall);
1246 }
1247
1248
1249 bool InsetTabular::tabularFeatures(BufferView * bv, string const & what)
1250 {
1251         LyXTabular::Feature action = LyXTabular::LAST_ACTION;
1252
1253         int i = 0;
1254         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
1255                 string const tmp = tabularFeature[i].feature;
1256
1257                 if (tmp == what.substr(0, tmp.length())) {
1258                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
1259                         //tabularFeatures[i].feature.length())) {
1260                         action = tabularFeature[i].action;
1261                         break;
1262                 }
1263         }
1264         if (action == LyXTabular::LAST_ACTION)
1265                 return false;
1266
1267         string const val =
1268                 ltrim(what.substr(tabularFeature[i].feature.length()));
1269         tabularFeatures(bv, action, val);
1270         return true;
1271 }
1272
1273 namespace {
1274
1275 void checkLongtableSpecial(LyXTabular::ltType & ltt,
1276                           string const & special, bool & flag)
1277 {
1278         if (special == "dl_above") {
1279                 ltt.topDL = flag;
1280                 ltt.set = false;
1281         } else if (special == "dl_below") {
1282                 ltt.bottomDL = flag;
1283                 ltt.set = false;
1284         } else if (special == "empty") {
1285                 ltt.empty = flag;
1286                 ltt.set = false;
1287         } else if (flag) {
1288                 ltt.empty = false;
1289                 ltt.set = true;
1290         }
1291 }
1292
1293 } // anon namespace
1294
1295
1296 void InsetTabular::tabularFeatures(BufferView * bv,
1297         LyXTabular::Feature feature, string const & value)
1298 {
1299         int sel_col_start;
1300         int sel_col_end;
1301         int sel_row_start;
1302         int sel_row_end;
1303         bool setLines = false;
1304         LyXAlignment setAlign = LYX_ALIGN_LEFT;
1305         LyXTabular::VAlignment setVAlign = LyXTabular::LYX_VALIGN_TOP;
1306
1307         switch (feature) {
1308
1309         case LyXTabular::M_ALIGN_LEFT:
1310         case LyXTabular::ALIGN_LEFT:
1311                 setAlign = LYX_ALIGN_LEFT;
1312                 break;
1313
1314         case LyXTabular::M_ALIGN_RIGHT:
1315         case LyXTabular::ALIGN_RIGHT:
1316                 setAlign = LYX_ALIGN_RIGHT;
1317                 break;
1318
1319         case LyXTabular::M_ALIGN_CENTER:
1320         case LyXTabular::ALIGN_CENTER:
1321                 setAlign = LYX_ALIGN_CENTER;
1322                 break;
1323
1324         case LyXTabular::ALIGN_BLOCK:
1325                 setAlign = LYX_ALIGN_BLOCK;
1326                 break;
1327
1328         case LyXTabular::M_VALIGN_TOP:
1329         case LyXTabular::VALIGN_TOP:
1330                 setVAlign = LyXTabular::LYX_VALIGN_TOP;
1331                 break;
1332
1333         case LyXTabular::M_VALIGN_BOTTOM:
1334         case LyXTabular::VALIGN_BOTTOM:
1335                 setVAlign = LyXTabular::LYX_VALIGN_BOTTOM;
1336                 break;
1337
1338         case LyXTabular::M_VALIGN_MIDDLE:
1339         case LyXTabular::VALIGN_MIDDLE:
1340                 setVAlign = LyXTabular::LYX_VALIGN_MIDDLE;
1341                 break;
1342
1343         default:
1344                 break;
1345         }
1346
1347         if (hasSelection()) {
1348                 getSelection(sel_row_start, sel_row_end, sel_col_start, sel_col_end);
1349         } else {
1350                 sel_col_start = sel_col_end = tabular.column_of_cell(actcell);
1351                 sel_row_start = sel_row_end = tabular.row_of_cell(actcell);
1352         }
1353         recordUndo(bv, Undo::ATOMIC);
1354
1355         int row =  tabular.row_of_cell(actcell);
1356         int column = tabular.column_of_cell(actcell);
1357         bool flag = true;
1358         LyXTabular::ltType ltt;
1359
1360         switch (feature) {
1361
1362         case LyXTabular::SET_PWIDTH: {
1363                 LyXLength const len(value);
1364                 tabular.setColumnPWidth(actcell, len);
1365                 if (len.zero()
1366                     && tabular.getAlignment(actcell, true) == LYX_ALIGN_BLOCK)
1367                         tabularFeatures(bv, LyXTabular::ALIGN_CENTER, string());
1368                 else if (!len.zero()
1369                          && tabular.getAlignment(actcell, true) != LYX_ALIGN_BLOCK)
1370                         tabularFeatures(bv, LyXTabular::ALIGN_BLOCK, string());
1371                 break;
1372         }
1373
1374         case LyXTabular::SET_MPWIDTH:
1375                 tabular.setMColumnPWidth(actcell, LyXLength(value));
1376                 break;
1377
1378         case LyXTabular::SET_SPECIAL_COLUMN:
1379         case LyXTabular::SET_SPECIAL_MULTI:
1380                 tabular.setAlignSpecial(actcell,value,feature);
1381                 break;
1382
1383         case LyXTabular::APPEND_ROW:
1384                 // append the row into the tabular
1385                 tabular.appendRow(bv->buffer()->params(), actcell);
1386                 tabular.setOwner(this);
1387                 break;
1388
1389         case LyXTabular::APPEND_COLUMN:
1390                 // append the column into the tabular
1391                 tabular.appendColumn(bv->buffer()->params(), actcell);
1392                 tabular.setOwner(this);
1393                 actcell = tabular.getCellNumber(row, column);
1394                 break;
1395
1396         case LyXTabular::DELETE_ROW:
1397                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1398                         tabular.deleteRow(sel_row_start);
1399                 if (sel_row_start >= tabular.rows())
1400                         --sel_row_start;
1401                 actcell = tabular.getCellNumber(sel_row_start, column);
1402                 clearSelection();
1403                 break;
1404
1405         case LyXTabular::DELETE_COLUMN:
1406                 for (int i = sel_col_start; i <= sel_col_end; ++i)
1407                         tabular.deleteColumn(sel_col_start);
1408                 if (sel_col_start >= tabular.columns())
1409                         --sel_col_start;
1410                 actcell = tabular.getCellNumber(row, sel_col_start);
1411                 clearSelection();
1412                 break;
1413
1414         case LyXTabular::M_TOGGLE_LINE_TOP:
1415                 flag = false;
1416         case LyXTabular::TOGGLE_LINE_TOP: {
1417                 bool lineSet = !tabular.topLine(actcell, flag);
1418                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1419                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1420                                 tabular.setTopLine(
1421                                         tabular.getCellNumber(i, j),
1422                                         lineSet, flag);
1423                 break;
1424         }
1425
1426         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
1427                 flag = false;
1428         case LyXTabular::TOGGLE_LINE_BOTTOM: {
1429                 bool lineSet = !tabular.bottomLine(actcell, flag);
1430                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1431                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1432                                 tabular.setBottomLine(
1433                                         tabular.getCellNumber(i, j),
1434                                         lineSet,
1435                                         flag);
1436                 break;
1437         }
1438
1439         case LyXTabular::M_TOGGLE_LINE_LEFT:
1440                 flag = false;
1441         case LyXTabular::TOGGLE_LINE_LEFT: {
1442                 bool lineSet = !tabular.leftLine(actcell, flag);
1443                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1444                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1445                                 tabular.setLeftLine(
1446                                         tabular.getCellNumber(i,j),
1447                                         lineSet,
1448                                         flag);
1449                 break;
1450         }
1451
1452         case LyXTabular::M_TOGGLE_LINE_RIGHT:
1453                 flag = false;
1454         case LyXTabular::TOGGLE_LINE_RIGHT: {
1455                 bool lineSet = !tabular.rightLine(actcell, flag);
1456                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1457                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1458                                 tabular.setRightLine(
1459                                         tabular.getCellNumber(i,j),
1460                                         lineSet,
1461                                         flag);
1462                 break;
1463         }
1464
1465         case LyXTabular::M_ALIGN_LEFT:
1466         case LyXTabular::M_ALIGN_RIGHT:
1467         case LyXTabular::M_ALIGN_CENTER:
1468                 flag = false;
1469         case LyXTabular::ALIGN_LEFT:
1470         case LyXTabular::ALIGN_RIGHT:
1471         case LyXTabular::ALIGN_CENTER:
1472         case LyXTabular::ALIGN_BLOCK:
1473                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1474                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1475                                 tabular.setAlignment(
1476                                         tabular.getCellNumber(i, j),
1477                                         setAlign,
1478                                         flag);
1479                 break;
1480
1481         case LyXTabular::M_VALIGN_TOP:
1482         case LyXTabular::M_VALIGN_BOTTOM:
1483         case LyXTabular::M_VALIGN_MIDDLE:
1484                 flag = false;
1485         case LyXTabular::VALIGN_TOP:
1486         case LyXTabular::VALIGN_BOTTOM:
1487         case LyXTabular::VALIGN_MIDDLE:
1488                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1489                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1490                                 tabular.setVAlignment(
1491                                         tabular.getCellNumber(i, j),
1492                                         setVAlign, flag);
1493                 break;
1494
1495         case LyXTabular::MULTICOLUMN: {
1496                 if (sel_row_start != sel_row_end) {
1497 #ifdef WITH_WARNINGS
1498 #warning Need I say it ? This is horrible.
1499 #endif
1500                         Alert::error(_("Error setting multicolumn"),
1501                                    _("You cannot set multicolumn vertically."));
1502                         return;
1503                 }
1504                 // just multicol for one Single Cell
1505                 if (!hasSelection()) {
1506                         // check wether we are completly in a multicol
1507                         if (tabular.isMultiColumn(actcell))
1508                                 tabular.unsetMultiColumn(actcell);
1509                         else
1510                                 tabular.setMultiColumn(bv->buffer(), actcell, 1);
1511                         break;
1512                 }
1513                 // we have a selection so this means we just add all this
1514                 // cells to form a multicolumn cell
1515                 int s_start;
1516                 int s_end;
1517
1518                 if (sel_cell_start > sel_cell_end) {
1519                         s_start = sel_cell_end;
1520                         s_end = sel_cell_start;
1521                 } else {
1522                         s_start = sel_cell_start;
1523                         s_end = sel_cell_end;
1524                 }
1525                 tabular.setMultiColumn(bv->buffer(), s_start, s_end - s_start + 1);
1526                 actcell = s_start;
1527                 clearSelection();
1528                 break;
1529         }
1530
1531         case LyXTabular::SET_ALL_LINES:
1532                 setLines = true;
1533         case LyXTabular::UNSET_ALL_LINES:
1534                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1535                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1536                                 tabular.setAllLines(
1537                                         tabular.getCellNumber(i,j), setLines);
1538                 break;
1539
1540         case LyXTabular::SET_LONGTABULAR:
1541                 tabular.setLongTabular(true);
1542                 break;
1543
1544         case LyXTabular::UNSET_LONGTABULAR:
1545                 tabular.setLongTabular(false);
1546                 break;
1547
1548         case LyXTabular::SET_ROTATE_TABULAR:
1549                 tabular.setRotateTabular(true);
1550                 break;
1551
1552         case LyXTabular::UNSET_ROTATE_TABULAR:
1553                 tabular.setRotateTabular(false);
1554                 break;
1555
1556         case LyXTabular::SET_ROTATE_CELL:
1557                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1558                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1559                                 tabular.setRotateCell(
1560                                         tabular.getCellNumber(i, j), true);
1561                 break;
1562
1563         case LyXTabular::UNSET_ROTATE_CELL:
1564                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1565                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1566                                 tabular.setRotateCell(
1567                                         tabular.getCellNumber(i, j), false);
1568                 break;
1569
1570         case LyXTabular::SET_USEBOX: {
1571                 LyXTabular::BoxType val = LyXTabular::BoxType(strToInt(value));
1572                 if (val == tabular.getUsebox(actcell))
1573                         val = LyXTabular::BOX_NONE;
1574                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1575                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1576                                 tabular.setUsebox(tabular.getCellNumber(i, j), val);
1577                 break;
1578         }
1579
1580         case LyXTabular::UNSET_LTFIRSTHEAD:
1581                 flag = false;
1582         case LyXTabular::SET_LTFIRSTHEAD:
1583                 tabular.getRowOfLTFirstHead(row, ltt);
1584                 checkLongtableSpecial(ltt, value, flag);
1585                 tabular.setLTHead(row, flag, ltt, true);
1586                 break;
1587
1588         case LyXTabular::UNSET_LTHEAD:
1589                 flag = false;
1590         case LyXTabular::SET_LTHEAD:
1591                 tabular.getRowOfLTHead(row, ltt);
1592                 checkLongtableSpecial(ltt, value, flag);
1593                 tabular.setLTHead(row, flag, ltt, false);
1594                 break;
1595
1596         case LyXTabular::UNSET_LTFOOT:
1597                 flag = false;
1598         case LyXTabular::SET_LTFOOT:
1599                 tabular.getRowOfLTFoot(row, ltt);
1600                 checkLongtableSpecial(ltt, value, flag);
1601                 tabular.setLTFoot(row, flag, ltt, false);
1602                 break;
1603
1604         case LyXTabular::UNSET_LTLASTFOOT:
1605                 flag = false;
1606         case LyXTabular::SET_LTLASTFOOT:
1607                 tabular.getRowOfLTLastFoot(row, ltt);
1608                 checkLongtableSpecial(ltt, value, flag);
1609                 tabular.setLTFoot(row, flag, ltt, true);
1610                 break;
1611
1612         case LyXTabular::SET_LTNEWPAGE:
1613                 tabular.setLTNewPage(row, !tabular.getLTNewPage(row));
1614                 break;
1615
1616         // dummy stuff just to avoid warnings
1617         case LyXTabular::LAST_ACTION:
1618                 break;
1619         }
1620
1621         updateLocal(bv);
1622         InsetTabularMailer(*this).updateDialog(bv);
1623 }
1624
1625
1626 bool InsetTabular::activateCellInset(BufferView * bv, int x, int y, bool behind)
1627 {
1628         UpdatableInset & inset = tabular.getCellInset(actcell);
1629         if (behind) {
1630 #warning metrics?
1631                 x = inset.x() + inset.width();
1632                 y = inset.descent();
1633         }
1634         inset.edit(bv, x, y);
1635         bv->cursor().push(&inset);
1636         if (!the_locking_inset)
1637                 return false;
1638         updateLocal(bv);
1639         return the_locking_inset;
1640 }
1641
1642
1643 bool InsetTabular::showInsetDialog(BufferView * bv) const
1644 {
1645         if (!the_locking_inset || !the_locking_inset->showInsetDialog(bv))
1646                 InsetTabularMailer(*this).showDialog(bv);
1647         return true;
1648 }
1649
1650
1651 void InsetTabular::openLayoutDialog(BufferView * bv) const
1652 {
1653 #warning Look here
1654 /*
1655         if (the_locking_inset) {
1656                 InsetTabular * inset = static_cast<InsetTabular *>
1657                         (the_locking_inset->getFirstLockingInsetOfType(TABULAR_CODE));
1658                 if (inset) {
1659                         inset->openLayoutDialog(bv);
1660                         return;
1661                 }
1662         }
1663 */
1664         InsetTabularMailer(*this).showDialog(bv);
1665 }
1666
1667
1668 //
1669 // function returns an object as defined in func_status.h:
1670 // states OK, Unknown, Disabled, On, Off.
1671 //
1672 FuncStatus InsetTabular::getStatus(string const & what) const
1673 {
1674         int action = LyXTabular::LAST_ACTION;
1675         FuncStatus status;
1676
1677         int i = 0;
1678         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
1679                 string const tmp = tabularFeature[i].feature;
1680                 if (tmp == what.substr(0, tmp.length())) {
1681                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
1682                         //   tabularFeatures[i].feature.length())) {
1683                         action = tabularFeature[i].action;
1684                         break;
1685                 }
1686         }
1687         if (action == LyXTabular::LAST_ACTION) {
1688                 status.clear();
1689                 status.unknown(true);
1690                 return status;
1691         }
1692
1693         string const argument
1694                 = ltrim(what.substr(tabularFeature[i].feature.length()));
1695
1696         int sel_row_start;
1697         int sel_row_end;
1698         int dummy;
1699         LyXTabular::ltType dummyltt;
1700         bool flag = true;
1701
1702         if (hasSelection())
1703                 getSelection(sel_row_start, sel_row_end, dummy, dummy);
1704         else
1705                 sel_row_start = sel_row_end = tabular.row_of_cell(actcell);
1706
1707         switch (action) {
1708         case LyXTabular::SET_PWIDTH:
1709         case LyXTabular::SET_MPWIDTH:
1710         case LyXTabular::SET_SPECIAL_COLUMN:
1711         case LyXTabular::SET_SPECIAL_MULTI:
1712         case LyXTabular::APPEND_ROW:
1713         case LyXTabular::APPEND_COLUMN:
1714         case LyXTabular::DELETE_ROW:
1715         case LyXTabular::DELETE_COLUMN:
1716         case LyXTabular::SET_ALL_LINES:
1717         case LyXTabular::UNSET_ALL_LINES:
1718                 status.clear();
1719                 return status;
1720
1721         case LyXTabular::MULTICOLUMN:
1722                 status.setOnOff(tabular.isMultiColumn(actcell));
1723                 break;
1724
1725         case LyXTabular::M_TOGGLE_LINE_TOP:
1726                 flag = false;
1727         case LyXTabular::TOGGLE_LINE_TOP:
1728                 status.setOnOff(tabular.topLine(actcell, flag));
1729                 break;
1730
1731         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
1732                 flag = false;
1733         case LyXTabular::TOGGLE_LINE_BOTTOM:
1734                 status.setOnOff(tabular.bottomLine(actcell, flag));
1735                 break;
1736
1737         case LyXTabular::M_TOGGLE_LINE_LEFT:
1738                 flag = false;
1739         case LyXTabular::TOGGLE_LINE_LEFT:
1740                 status.setOnOff(tabular.leftLine(actcell, flag));
1741                 break;
1742
1743         case LyXTabular::M_TOGGLE_LINE_RIGHT:
1744                 flag = false;
1745         case LyXTabular::TOGGLE_LINE_RIGHT:
1746                 status.setOnOff(tabular.rightLine(actcell, flag));
1747                 break;
1748
1749         case LyXTabular::M_ALIGN_LEFT:
1750                 flag = false;
1751         case LyXTabular::ALIGN_LEFT:
1752                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_LEFT);
1753                 break;
1754
1755         case LyXTabular::M_ALIGN_RIGHT:
1756                 flag = false;
1757         case LyXTabular::ALIGN_RIGHT:
1758                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_RIGHT);
1759                 break;
1760
1761         case LyXTabular::M_ALIGN_CENTER:
1762                 flag = false;
1763         case LyXTabular::ALIGN_CENTER:
1764                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_CENTER);
1765                 break;
1766
1767         case LyXTabular::ALIGN_BLOCK:
1768                 status.disabled(tabular.getPWidth(actcell).zero());
1769                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_BLOCK);
1770                 break;
1771
1772         case LyXTabular::M_VALIGN_TOP:
1773                 flag = false;
1774         case LyXTabular::VALIGN_TOP:
1775                 status.setOnOff(
1776                         tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_TOP);
1777                 break;
1778
1779         case LyXTabular::M_VALIGN_BOTTOM:
1780                 flag = false;
1781         case LyXTabular::VALIGN_BOTTOM:
1782                 status.setOnOff(
1783                         tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_BOTTOM);
1784                 break;
1785
1786         case LyXTabular::M_VALIGN_MIDDLE:
1787                 flag = false;
1788         case LyXTabular::VALIGN_MIDDLE:
1789                 status.setOnOff(
1790                         tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_MIDDLE);
1791                 break;
1792
1793         case LyXTabular::SET_LONGTABULAR:
1794                 status.setOnOff(tabular.isLongTabular());
1795                 break;
1796
1797         case LyXTabular::UNSET_LONGTABULAR:
1798                 status.setOnOff(!tabular.isLongTabular());
1799                 break;
1800
1801         case LyXTabular::SET_ROTATE_TABULAR:
1802                 status.setOnOff(tabular.getRotateTabular());
1803                 break;
1804
1805         case LyXTabular::UNSET_ROTATE_TABULAR:
1806                 status.setOnOff(!tabular.getRotateTabular());
1807                 break;
1808
1809         case LyXTabular::SET_ROTATE_CELL:
1810                 status.setOnOff(tabular.getRotateCell(actcell));
1811                 break;
1812
1813         case LyXTabular::UNSET_ROTATE_CELL:
1814                 status.setOnOff(!tabular.getRotateCell(actcell));
1815                 break;
1816
1817         case LyXTabular::SET_USEBOX:
1818                 status.setOnOff(strToInt(argument) == tabular.getUsebox(actcell));
1819                 break;
1820
1821         case LyXTabular::SET_LTFIRSTHEAD:
1822                 status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
1823                 break;
1824
1825         case LyXTabular::SET_LTHEAD:
1826                 status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
1827                 break;
1828
1829         case LyXTabular::SET_LTFOOT:
1830                 status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
1831                 break;
1832
1833         case LyXTabular::SET_LTLASTFOOT:
1834                 status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
1835                 break;
1836
1837         case LyXTabular::SET_LTNEWPAGE:
1838                 status.setOnOff(tabular.getLTNewPage(sel_row_start));
1839                 break;
1840
1841         default:
1842                 status.clear();
1843                 status.disabled(true);
1844                 break;
1845         }
1846         return status;
1847 }
1848
1849
1850 void InsetTabular::getLabelList(Buffer const & buffer,
1851                                 std::vector<string> & list) const
1852 {
1853         tabular.getLabelList(buffer, list);
1854 }
1855
1856
1857 bool InsetTabular::copySelection(BufferView * bv)
1858 {
1859         if (!hasSelection())
1860                 return false;
1861
1862         int sel_col_start = tabular.column_of_cell(sel_cell_start);
1863         int sel_col_end = tabular.column_of_cell(sel_cell_end);
1864         if (sel_col_start > sel_col_end) {
1865                 sel_col_start = sel_col_end;
1866                 sel_col_end = tabular.right_column_of_cell(sel_cell_start);
1867         } else {
1868                 sel_col_end = tabular.right_column_of_cell(sel_cell_end);
1869         }
1870
1871         int sel_row_start = tabular.row_of_cell(sel_cell_start);
1872         int sel_row_end = tabular.row_of_cell(sel_cell_end);
1873         if (sel_row_start > sel_row_end)
1874                 swap(sel_row_start, sel_row_end);
1875
1876         paste_tabular.reset(new LyXTabular(tabular));
1877         paste_tabular->setOwner(this);
1878
1879         for (int i = 0; i < sel_row_start; ++i)
1880                 paste_tabular->deleteRow(0);
1881
1882         int const rows = sel_row_end - sel_row_start + 1;
1883         while (paste_tabular->rows() > rows)
1884                 paste_tabular->deleteRow(rows);
1885
1886         paste_tabular->setTopLine(0, true, true);
1887         paste_tabular->setBottomLine(paste_tabular->getFirstCellInRow(rows - 1),
1888                                      true, true);
1889
1890         for (int i = 0; i < sel_col_start; ++i)
1891                 paste_tabular->deleteColumn(0);
1892
1893         int const columns = sel_col_end - sel_col_start + 1;
1894         while (paste_tabular->columns() > columns)
1895                 paste_tabular->deleteColumn(columns);
1896
1897         paste_tabular->setLeftLine(0, true, true);
1898         paste_tabular->setRightLine(paste_tabular->getLastCellInRow(0),
1899                                     true, true);
1900
1901         ostringstream os;
1902         OutputParams const runparams;   
1903         paste_tabular->plaintext(*bv->buffer(), os, runparams,
1904                                  ownerPar(*bv->buffer(), this).params().depth(), true, '\t');
1905         bv->stuffClipboard(os.str());
1906         return true;
1907 }
1908
1909
1910 bool InsetTabular::pasteSelection(BufferView * bv)
1911 {
1912         if (!paste_tabular)
1913                 return false;
1914
1915         for (int r1 = 0, r2 = actrow;
1916              r1 < paste_tabular->rows() && r2 < tabular.rows();
1917              ++r1, ++r2) {
1918                 for (int c1 = 0, c2 = actcol;
1919                     c1 < paste_tabular->columns() && c2 < tabular.columns();
1920                     ++c1, ++c2) {
1921                         if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
1922                             tabular.isPartOfMultiColumn(r2, c2))
1923                                 continue;
1924                         if (paste_tabular->isPartOfMultiColumn(r1, c1)) {
1925                                 --c2;
1926                                 continue;
1927                         }
1928                         if (tabular.isPartOfMultiColumn(r2, c2)) {
1929                                 --c1;
1930                                 continue;
1931                         }
1932                         InsetText & inset = tabular.getCellInset(r2, c2);
1933                         inset = paste_tabular->getCellInset(r1, c1);
1934                         inset.setOwner(this);
1935                         inset.markNew();
1936                 }
1937         }
1938         return true;
1939 }
1940
1941
1942 bool InsetTabular::cutSelection(BufferParams const & bp)
1943 {
1944         if (!hasSelection())
1945                 return false;
1946
1947         int sel_col_start = tabular.column_of_cell(sel_cell_start);
1948         int sel_col_end = tabular.column_of_cell(sel_cell_end);
1949         if (sel_col_start > sel_col_end) {
1950                 sel_col_start = sel_col_end;
1951                 sel_col_end = tabular.right_column_of_cell(sel_cell_start);
1952         } else {
1953                 sel_col_end = tabular.right_column_of_cell(sel_cell_end);
1954         }
1955
1956         int sel_row_start = tabular.row_of_cell(sel_cell_start);
1957         int sel_row_end = tabular.row_of_cell(sel_cell_end);
1958
1959         if (sel_row_start > sel_row_end)
1960                 swap(sel_row_start, sel_row_end);
1961
1962         if (sel_cell_start > sel_cell_end)
1963                 swap(sel_cell_start, sel_cell_end);
1964
1965         for (int i = sel_row_start; i <= sel_row_end; ++i)
1966                 for (int j = sel_col_start; j <= sel_col_end; ++j)
1967                         tabular.getCellInset(tabular.getCellNumber(i, j))
1968                                 .clear(bp.tracking_changes);
1969         return true;
1970 }
1971
1972
1973 bool InsetTabular::isRightToLeft(BufferView * bv)
1974 {
1975         return bv->getParentLanguage(this)->RightToLeft();
1976 }
1977
1978
1979 int InsetTabular::scroll(bool recursive) const
1980 {
1981         int sx = UpdatableInset::scroll(false);
1982
1983         if (recursive && the_locking_inset)
1984                 sx += the_locking_inset->scroll(recursive);
1985
1986         return sx;
1987 }
1988
1989
1990 void InsetTabular::getSelection(int & srow, int & erow,
1991                                 int & scol, int & ecol) const
1992 {
1993         int const start = hasSelection() ? sel_cell_start : actcell;
1994         int const end = hasSelection() ? sel_cell_end : actcell;
1995
1996         srow = tabular.row_of_cell(start);
1997         erow = tabular.row_of_cell(end);
1998         if (srow > erow)
1999                 swap(srow, erow);
2000
2001         scol = tabular.column_of_cell(start);
2002         ecol = tabular.column_of_cell(end);
2003         if (scol > ecol)
2004                 swap(scol, ecol);
2005         else
2006                 ecol = tabular.right_column_of_cell(end);
2007 }
2008
2009
2010 ParagraphList * InsetTabular::getParagraphs(int i) const
2011 {
2012         return i < tabular.getNumberOfCells()
2013                 ? tabular.getCellInset(i).getParagraphs(0)
2014                 : 0;
2015 }
2016
2017
2018 int InsetTabular::numParagraphs() const
2019 {
2020         return tabular.getNumberOfCells();
2021 }
2022
2023
2024 LyXText * InsetTabular::getText(int i) const
2025 {
2026         return i < tabular.getNumberOfCells()
2027                 ? tabular.getCellInset(i).getText(0)
2028                 : 0;
2029 }
2030
2031
2032 void InsetTabular::markErased()
2033 {
2034         for (int cell = 0; cell < tabular.getNumberOfCells(); ++cell)
2035                 tabular.getCellInset(cell).markErased();
2036 }
2037
2038
2039 bool InsetTabular::insetAllowed(InsetOld::Code) const
2040 {
2041         return false;
2042 }
2043
2044
2045 bool InsetTabular::forceDefaultParagraphs(InsetOld const * in) const
2046 {
2047         const int cell = tabular.getCellFromInset(in);
2048
2049         if (cell != -1)
2050                 return tabular.getPWidth(cell).zero();
2051
2052         // this is a workaround for a crash (New, Insert->Tabular,
2053         // Insert->FootNote)
2054         if (!owner())
2055                 return false;
2056
2057         // well we didn't obviously find it so maybe our owner knows more
2058         BOOST_ASSERT(owner());
2059         return owner()->forceDefaultParagraphs(in);
2060 }
2061
2062
2063 bool InsetTabular::insertAsciiString(BufferView * bv, string const & buf,
2064                                      bool usePaste)
2065 {
2066         if (buf.length() <= 0)
2067                 return true;
2068
2069         int cols = 1;
2070         int rows = 1;
2071         int maxCols = 1;
2072         string::size_type len = buf.length();
2073         string::size_type p = 0;
2074
2075         while (p < len && (p = buf.find_first_of("\t\n", p)) != string::npos) {
2076                 switch (buf[p]) {
2077                 case '\t':
2078                         ++cols;
2079                         break;
2080                 case '\n':
2081                         if (p + 1 < len)
2082                                 ++rows;
2083                         maxCols = max(cols, maxCols);
2084                         cols = 1;
2085                         break;
2086                 }
2087                 ++p;
2088         }
2089         maxCols = max(cols, maxCols);
2090         LyXTabular * loctab;
2091         int cell = 0;
2092         int ocol = 0;
2093         int row = 0;
2094         if (usePaste) {
2095                 paste_tabular.reset(
2096                         new LyXTabular(bv->buffer()->params(), rows, maxCols)
2097                         );
2098
2099                 paste_tabular->setOwner(this);
2100                 loctab = paste_tabular.get();
2101                 cols = 0;
2102         } else {
2103                 loctab = &tabular;
2104                 cell = actcell;
2105                 ocol = actcol;
2106                 row = actrow;
2107         }
2108
2109         string::size_type op = 0;
2110         int cells = loctab->getNumberOfCells();
2111         p = 0;
2112         cols = ocol;
2113         rows = loctab->rows();
2114         int const columns = loctab->columns();
2115
2116         while (cell < cells && p < len && row < rows &&
2117                (p = buf.find_first_of("\t\n", p)) != string::npos)
2118         {
2119                 if (p >= len)
2120                         break;
2121                 switch (buf[p]) {
2122                 case '\t':
2123                         // we can only set this if we are not too far right
2124                         if (cols < columns) {
2125                                 InsetText & inset = loctab->getCellInset(cell);
2126                                 LyXFont const font = inset.text_.
2127                                         getFont(inset.paragraphs.begin(), 0);
2128                                 inset.setText(buf.substr(op, p - op), font);
2129                                 ++cols;
2130                                 ++cell;
2131                         }
2132                         break;
2133                 case '\n':
2134                         // we can only set this if we are not too far right
2135                         if (cols < columns) {
2136                                 InsetText & inset = tabular.getCellInset(cell);
2137                                 LyXFont const font = inset.text_.
2138                                         getFont(inset.paragraphs.begin(), 0);
2139                                 inset.setText(buf.substr(op, p - op), font);
2140                         }
2141                         cols = ocol;
2142                         ++row;
2143                         if (row < rows)
2144                                 cell = loctab->getCellNumber(row, cols);
2145                         break;
2146                 }
2147                 ++p;
2148                 op = p;
2149         }
2150         // check for the last cell if there is no trailing '\n'
2151         if (cell < cells && op < len) {
2152                 InsetText & inset = loctab->getCellInset(cell);
2153                 LyXFont const font = inset.text_.getFont(inset.paragraphs.begin(), 0);
2154                 inset.setText(buf.substr(op, len - op), font);
2155         }
2156
2157         return true;
2158 }
2159
2160
2161 void InsetTabular::addPreview(PreviewLoader & loader) const
2162 {
2163         int const rows = tabular.rows();
2164         int const columns = tabular.columns();
2165         for (int i = 0; i < rows; ++i) {
2166                 for (int j = 0; j < columns; ++j)
2167                         tabular.getCellInset(i, j).addPreview(loader);
2168         }
2169 }
2170
2171
2172 string const InsetTabularMailer::name_("tabular");
2173
2174 InsetTabularMailer::InsetTabularMailer(InsetTabular const & inset)
2175         : inset_(const_cast<InsetTabular &>(inset))
2176 {}
2177
2178
2179 string const InsetTabularMailer::inset2string(Buffer const &) const
2180 {
2181         return params2string(inset_);
2182 }
2183
2184
2185 int InsetTabularMailer::string2params(string const & in, InsetTabular & inset)
2186 {
2187         istringstream data(in);
2188         LyXLex lex(0,0);
2189         lex.setStream(data);
2190
2191 #warning CHECK verify that this is a sane value to return.
2192         if (in.empty())
2193                 return -1;
2194
2195         if (lex.isOK()) {
2196                 lex.next();
2197                 string const token = lex.getString();
2198                 if (token != name_)
2199                         return -1;
2200         }
2201
2202         int cell = -1;
2203         if (lex.isOK()) {
2204                 lex.next();
2205                 string const token = lex.getString();
2206                 if (token != "\\active_cell")
2207                         return -1;
2208                 lex.next();
2209                 cell = lex.getInteger();
2210         }
2211
2212         // This is part of the inset proper that is usually swallowed
2213         // by Buffer::readInset
2214         if (lex.isOK()) {
2215                 lex.next();
2216                 string const token = lex.getString();
2217                 if (token != "Tabular")
2218                         return -1;
2219         }
2220
2221         if (!lex.isOK())
2222                 return -1;
2223
2224         Buffer const & buffer = inset.buffer();
2225         inset.read(buffer, lex);
2226
2227         // We can't set the active cell, but we can tell the frontend
2228         // what it is.
2229         return cell;
2230 }
2231
2232
2233 string const InsetTabularMailer::params2string(InsetTabular const & inset)
2234 {
2235         ostringstream data;
2236         data << name_ << " \\active_cell " << inset.getActCell() << '\n';
2237         inset.write(inset.buffer(), data);
2238         data << "\\end_inset\n";
2239         return data.str();
2240 }