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