]> git.lyx.org Git - lyx.git/blob - src/insets/insettabular.C
the update/updateInset merge
[lyx.git] / src / insets / insettabular.C
1 /**
2  * \file insettabular.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "insettabular.h"
14
15 #include "buffer.h"
16 #include "bufferparams.h"
17 #include "BufferView.h"
18 #include "cursor.h"
19 #include "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         top_x = x;
259         top_baseline = 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_ - top_x + 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                     < top_baseline + 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 (top_baseline < 0) {
642                         bv->scrollDocView(bv->top_y() - bv->painter().paperHeight());
643                         if (top_baseline > 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_ - top_x;
972         y = TEXT_TO_INSET_OFFSET + cursory_;
973 }
974
975
976 void InsetTabular::fitInsetCursor(BufferView * bv) const
977 {
978         if (the_locking_inset) {
979                 the_locking_inset->fitInsetCursor(bv);
980                 return;
981         }
982
983         LyXFont font;
984         int const asc = font_metrics::maxAscent(font);
985         int const desc = font_metrics::maxDescent(font);
986         resetPos(bv);
987
988         bv->fitLockedInsetCursor(cursorx_, cursory_, asc, desc);
989 }
990
991
992 void InsetTabular::setPos(BufferView * bv, int x, int y) const
993 {
994         cursory_ = 0;
995         actcell = 0;
996         actrow = 0;
997         actcol = 0;
998         int ly = tabular.getDescentOfRow(actrow);
999
1000         // first search the right row
1001         while (ly < y && actrow + 1 < tabular.rows()) {
1002                 cursory_ += tabular.getDescentOfRow(actrow) +
1003                                  tabular.getAscentOfRow(actrow + 1) +
1004                                  tabular.getAdditionalHeight(actrow + 1);
1005                 ++actrow;
1006                 ly = cursory_ + tabular.getDescentOfRow(actrow);
1007         }
1008         actcell = tabular.getCellNumber(actrow, actcol);
1009
1010         // now search the right column
1011         int lx = tabular.getWidthOfColumn(actcell) -
1012                 tabular.getAdditionalWidth(actcell);
1013
1014         for (; !tabular.isLastCellInRow(actcell) && lx < x; ++actcell)
1015                 lx += tabular.getWidthOfColumn(actcell + 1)
1016                         + tabular.getAdditionalWidth(actcell);
1017
1018         cursorx_ = lx - tabular.getWidthOfColumn(actcell) + top_x + 2;
1019         resetPos(bv);
1020 }
1021
1022
1023 int InsetTabular::getCellXPos(int cell) const
1024 {
1025         int c = cell;
1026
1027         for (; !tabular.isFirstCellInRow(c); --c)
1028                 ;
1029         int lx = tabular.getWidthOfColumn(cell);
1030         for (; c < cell; ++c)
1031                 lx += tabular.getWidthOfColumn(c);
1032
1033         return lx - tabular.getWidthOfColumn(cell) + top_x;
1034 }
1035
1036
1037 void InsetTabular::resetPos(BufferView * bv) const
1038 {
1039 #ifdef WITH_WARNINGS
1040 #warning This should be fixed in the right manner (20011128 Jug)
1041 #endif
1042         // fast hack to fix infinite repaintings!
1043         if (in_reset_pos > 0)
1044                 return;
1045
1046         int cell = 0;
1047         actcol = tabular.column_of_cell(actcell);
1048         actrow = 0;
1049         cursory_ = 0;
1050         for (; cell < actcell && !tabular.isLastRow(cell); ++cell) {
1051                 if (tabular.isLastCellInRow(cell)) {
1052                         cursory_ += tabular.getDescentOfRow(actrow) +
1053                                          tabular.getAscentOfRow(actrow + 1) +
1054                                          tabular.getAdditionalHeight(actrow + 1);
1055                         ++actrow;
1056                 }
1057         }
1058
1059         // we need this only from here on!!!
1060         ++in_reset_pos;
1061         int const offset = ADD_TO_TABULAR_WIDTH + 2;
1062         int new_x = getCellXPos(actcell) + offset;
1063         int old_x = cursorx_;
1064         cursorx_ = new_x;
1065 //    cursor.x(getCellXPos(actcell) + offset);
1066         if (actcol < tabular.columns() - 1 && scroll(false) &&
1067                 tabular.getWidthOfTabular() < bv->workWidth()-20)
1068         {
1069                 scroll(bv, 0.0F);
1070                 updateLocal(bv);
1071         } else if (the_locking_inset &&
1072                  tabular.getWidthOfColumn(actcell) > bv->workWidth() - 20)
1073         {
1074                 int xx = cursorx_ - offset;
1075                 if (xx > bv->workWidth()-20) {
1076                         scroll(bv, - xx + bv->workWidth() - 60);
1077                         updateLocal(bv);
1078                 } else if (xx < 20) {
1079                         if (xx < 0)
1080                                 xx = -xx + 60;
1081                         else
1082                                 xx = 60;
1083                         scroll(bv, xx);
1084                         updateLocal(bv);
1085                 }
1086         } else if (cursorx_ - offset > 20 &&
1087                    cursorx_ - offset + tabular.getWidthOfColumn(actcell)
1088                    > bv->workWidth() - 20) {
1089                 scroll(bv, - tabular.getWidthOfColumn(actcell) - 20);
1090                 updateLocal(bv);
1091         } else if (cursorx_ - offset < 20) {
1092                 scroll(bv, 20 - cursorx_ + offset);
1093                 updateLocal(bv);
1094         } else if (scroll() && top_x > 20 &&
1095                    top_x + tabular.getWidthOfTabular() > bv->workWidth() - 20) {
1096                 scroll(bv, old_x - cursorx_);
1097                 updateLocal(bv);
1098         }
1099         InsetTabularMailer(*this).updateDialog(bv);
1100         in_reset_pos = 0;
1101 }
1102
1103
1104 DispatchResult InsetTabular::moveRight(BufferView * bv, bool lock)
1105 {
1106         if (lock) {
1107                 if (activateCellInset(bv))
1108                         return DispatchResult(true, true);
1109         } else {
1110                 bool moved = isRightToLeft(bv)
1111                         ? movePrevCell(bv) : moveNextCell(bv);
1112                 if (!moved)
1113                         return DispatchResult(false, FINISHED_RIGHT);
1114                 if (lock && activateCellInset(bv))
1115                         return DispatchResult(true, true);
1116         }
1117         resetPos(bv);
1118         return DispatchResult(true);
1119 }
1120
1121
1122 DispatchResult InsetTabular::moveLeft(BufferView * bv, bool lock)
1123 {
1124         bool moved = isRightToLeft(bv) ? moveNextCell(bv) : movePrevCell(bv);
1125         if (!moved)
1126                 return DispatchResult(false, FINISHED);
1127         // behind the inset
1128         if (lock && activateCellInset(bv, 0, 0, true))
1129                 return DispatchResult(true, true);
1130         resetPos(bv);
1131         return DispatchResult(true);
1132 }
1133
1134
1135 DispatchResult InsetTabular::moveUp(BufferView * bv, bool lock)
1136 {
1137         int const ocell = actcell;
1138         actcell = tabular.getCellAbove(actcell);
1139         if (actcell == ocell) { // we moved out of the inset
1140                 return DispatchResult(false, FINISHED_UP);
1141         }
1142         resetPos(bv);
1143         if (lock)
1144                 activateCellInset(bv, bv->x_target(), 0);
1145         return DispatchResult(true, true);
1146 }
1147
1148
1149 DispatchResult InsetTabular::moveDown(BufferView * bv, bool lock)
1150 {
1151         int const ocell = actcell;
1152         actcell = tabular.getCellBelow(actcell);
1153         if (actcell == ocell) { // we moved out of the inset
1154                 return DispatchResult(false, FINISHED_DOWN);
1155         }
1156         resetPos(bv);
1157         if (lock)
1158                 activateCellInset(bv, bv->x_target(), 0);
1159         return DispatchResult(true, true);
1160 }
1161
1162
1163 bool InsetTabular::moveNextCell(BufferView * bv, bool lock)
1164 {
1165         if (isRightToLeft(bv)) {
1166                 if (tabular.isFirstCellInRow(actcell)) {
1167                         int row = tabular.row_of_cell(actcell);
1168                         if (row == tabular.rows() - 1)
1169                                 return false;
1170                         actcell = tabular.getLastCellInRow(row);
1171                         actcell = tabular.getCellBelow(actcell);
1172                 } else {
1173                         if (!actcell)
1174                                 return false;
1175                         --actcell;
1176                 }
1177         } else {
1178                 if (tabular.isLastCell(actcell))
1179                         return false;
1180                 ++actcell;
1181         }
1182         if (lock) {
1183                 bool rtl = tabular.getCellInset(actcell).paragraphs.begin()->
1184                         isRightToLeftPar(bv->buffer()->params());
1185                 activateCellInset(bv, 0, 0, !rtl);
1186         }
1187         resetPos(bv);
1188         return true;
1189 }
1190
1191
1192 bool InsetTabular::movePrevCell(BufferView * bv, bool lock)
1193 {
1194         lyxerr << "move prevcell 1" << endl;
1195         if (isRightToLeft(bv)) {
1196                 lyxerr << "move prevcell a" << endl;
1197                 if (tabular.isLastCellInRow(actcell)) {
1198                         int row = tabular.row_of_cell(actcell);
1199                         if (row == 0)
1200                                 return false;
1201                         actcell = tabular.getFirstCellInRow(row);
1202                         actcell = tabular.getCellAbove(actcell);
1203                 } else {
1204                         if (tabular.isLastCell(actcell))
1205                                 return false;
1206                         ++actcell;
1207                 }
1208         } else {
1209                 lyxerr << "move prevcell b" << endl;
1210                 if (actcell == 0) // first cell
1211                         return false;
1212                 --actcell;
1213         }
1214         lyxerr << "move prevcell 2" << endl;
1215         if (lock) {
1216                 bool rtl = tabular.getCellInset(actcell).paragraphs.begin()->
1217                         isRightToLeftPar(bv->buffer()->params());
1218                 activateCellInset(bv, 0, 0, !rtl);
1219         }
1220         lyxerr << "move prevcell 3" << endl;
1221         resetPos(bv);
1222         lyxerr << "move prevcell 4" << endl;
1223         return true;
1224 }
1225
1226
1227 void InsetTabular::setFont(BufferView * bv, LyXFont const & font, bool tall,
1228                            bool selectall)
1229 {
1230         if (selectall) {
1231                 setSelection(0, tabular.getNumberOfCells() - 1);
1232         }
1233         if (hasSelection()) {
1234                 recordUndo(bv, Undo::ATOMIC);
1235                 bool const frozen = undo_frozen;
1236                 if (!frozen)
1237                         freezeUndo();
1238                 // apply the fontchange on the whole selection
1239                 int sel_row_start;
1240                 int sel_row_end;
1241                 int sel_col_start;
1242                 int sel_col_end;
1243                 getSelection(sel_row_start, sel_row_end, sel_col_start, sel_col_end);
1244                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1245                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1246                                 tabular.getCellInset(i, j).setFont(bv, font, tall, true);
1247
1248                 if (!frozen)
1249                         unFreezeUndo();
1250                 if (selectall)
1251                         clearSelection();
1252                 updateLocal(bv);
1253         }
1254         if (the_locking_inset)
1255                 the_locking_inset->setFont(bv, font, tall);
1256 }
1257
1258
1259 bool InsetTabular::tabularFeatures(BufferView * bv, string const & what)
1260 {
1261         LyXTabular::Feature action = LyXTabular::LAST_ACTION;
1262
1263         int i = 0;
1264         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
1265                 string const tmp = tabularFeature[i].feature;
1266
1267                 if (tmp == what.substr(0, tmp.length())) {
1268                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
1269                         //tabularFeatures[i].feature.length())) {
1270                         action = tabularFeature[i].action;
1271                         break;
1272                 }
1273         }
1274         if (action == LyXTabular::LAST_ACTION)
1275                 return false;
1276
1277         string const val =
1278                 ltrim(what.substr(tabularFeature[i].feature.length()));
1279         tabularFeatures(bv, action, val);
1280         return true;
1281 }
1282
1283 namespace {
1284
1285 void checkLongtableSpecial(LyXTabular::ltType & ltt,
1286                           string const & special, bool & flag)
1287 {
1288         if (special == "dl_above") {
1289                 ltt.topDL = flag;
1290                 ltt.set = false;
1291         } else if (special == "dl_below") {
1292                 ltt.bottomDL = flag;
1293                 ltt.set = false;
1294         } else if (special == "empty") {
1295                 ltt.empty = flag;
1296                 ltt.set = false;
1297         } else if (flag) {
1298                 ltt.empty = false;
1299                 ltt.set = true;
1300         }
1301 }
1302
1303 } // anon namespace
1304
1305
1306 void InsetTabular::tabularFeatures(BufferView * bv,
1307         LyXTabular::Feature feature, string const & value)
1308 {
1309         int sel_col_start;
1310         int sel_col_end;
1311         int sel_row_start;
1312         int sel_row_end;
1313         bool setLines = false;
1314         LyXAlignment setAlign = LYX_ALIGN_LEFT;
1315         LyXTabular::VAlignment setVAlign = LyXTabular::LYX_VALIGN_TOP;
1316
1317         switch (feature) {
1318
1319         case LyXTabular::M_ALIGN_LEFT:
1320         case LyXTabular::ALIGN_LEFT:
1321                 setAlign = LYX_ALIGN_LEFT;
1322                 break;
1323
1324         case LyXTabular::M_ALIGN_RIGHT:
1325         case LyXTabular::ALIGN_RIGHT:
1326                 setAlign = LYX_ALIGN_RIGHT;
1327                 break;
1328
1329         case LyXTabular::M_ALIGN_CENTER:
1330         case LyXTabular::ALIGN_CENTER:
1331                 setAlign = LYX_ALIGN_CENTER;
1332                 break;
1333
1334         case LyXTabular::ALIGN_BLOCK:
1335                 setAlign = LYX_ALIGN_BLOCK;
1336                 break;
1337
1338         case LyXTabular::M_VALIGN_TOP:
1339         case LyXTabular::VALIGN_TOP:
1340                 setVAlign = LyXTabular::LYX_VALIGN_TOP;
1341                 break;
1342
1343         case LyXTabular::M_VALIGN_BOTTOM:
1344         case LyXTabular::VALIGN_BOTTOM:
1345                 setVAlign = LyXTabular::LYX_VALIGN_BOTTOM;
1346                 break;
1347
1348         case LyXTabular::M_VALIGN_MIDDLE:
1349         case LyXTabular::VALIGN_MIDDLE:
1350                 setVAlign = LyXTabular::LYX_VALIGN_MIDDLE;
1351                 break;
1352
1353         default:
1354                 break;
1355         }
1356
1357         if (hasSelection()) {
1358                 getSelection(sel_row_start, sel_row_end, sel_col_start, sel_col_end);
1359         } else {
1360                 sel_col_start = sel_col_end = tabular.column_of_cell(actcell);
1361                 sel_row_start = sel_row_end = tabular.row_of_cell(actcell);
1362         }
1363         recordUndo(bv, Undo::ATOMIC);
1364
1365         int row =  tabular.row_of_cell(actcell);
1366         int column = tabular.column_of_cell(actcell);
1367         bool flag = true;
1368         LyXTabular::ltType ltt;
1369
1370         switch (feature) {
1371
1372         case LyXTabular::SET_PWIDTH: {
1373                 LyXLength const len(value);
1374                 tabular.setColumnPWidth(actcell, len);
1375                 if (len.zero()
1376                     && tabular.getAlignment(actcell, true) == LYX_ALIGN_BLOCK)
1377                         tabularFeatures(bv, LyXTabular::ALIGN_CENTER, string());
1378                 else if (!len.zero()
1379                          && tabular.getAlignment(actcell, true) != LYX_ALIGN_BLOCK)
1380                         tabularFeatures(bv, LyXTabular::ALIGN_BLOCK, string());
1381                 break;
1382         }
1383
1384         case LyXTabular::SET_MPWIDTH:
1385                 tabular.setMColumnPWidth(actcell, LyXLength(value));
1386                 break;
1387
1388         case LyXTabular::SET_SPECIAL_COLUMN:
1389         case LyXTabular::SET_SPECIAL_MULTI:
1390                 tabular.setAlignSpecial(actcell,value,feature);
1391                 break;
1392
1393         case LyXTabular::APPEND_ROW:
1394                 // append the row into the tabular
1395                 tabular.appendRow(bv->buffer()->params(), actcell);
1396                 tabular.setOwner(this);
1397                 break;
1398
1399         case LyXTabular::APPEND_COLUMN:
1400                 // append the column into the tabular
1401                 tabular.appendColumn(bv->buffer()->params(), actcell);
1402                 tabular.setOwner(this);
1403                 actcell = tabular.getCellNumber(row, column);
1404                 break;
1405
1406         case LyXTabular::DELETE_ROW:
1407                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1408                         tabular.deleteRow(sel_row_start);
1409                 if (sel_row_start >= tabular.rows())
1410                         --sel_row_start;
1411                 actcell = tabular.getCellNumber(sel_row_start, column);
1412                 clearSelection();
1413                 break;
1414
1415         case LyXTabular::DELETE_COLUMN:
1416                 for (int i = sel_col_start; i <= sel_col_end; ++i)
1417                         tabular.deleteColumn(sel_col_start);
1418                 if (sel_col_start >= tabular.columns())
1419                         --sel_col_start;
1420                 actcell = tabular.getCellNumber(row, sel_col_start);
1421                 clearSelection();
1422                 break;
1423
1424         case LyXTabular::M_TOGGLE_LINE_TOP:
1425                 flag = false;
1426         case LyXTabular::TOGGLE_LINE_TOP: {
1427                 bool lineSet = !tabular.topLine(actcell, flag);
1428                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1429                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1430                                 tabular.setTopLine(
1431                                         tabular.getCellNumber(i, j),
1432                                         lineSet, flag);
1433                 break;
1434         }
1435
1436         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
1437                 flag = false;
1438         case LyXTabular::TOGGLE_LINE_BOTTOM: {
1439                 bool lineSet = !tabular.bottomLine(actcell, flag);
1440                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1441                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1442                                 tabular.setBottomLine(
1443                                         tabular.getCellNumber(i, j),
1444                                         lineSet,
1445                                         flag);
1446                 break;
1447         }
1448
1449         case LyXTabular::M_TOGGLE_LINE_LEFT:
1450                 flag = false;
1451         case LyXTabular::TOGGLE_LINE_LEFT: {
1452                 bool lineSet = !tabular.leftLine(actcell, flag);
1453                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1454                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1455                                 tabular.setLeftLine(
1456                                         tabular.getCellNumber(i,j),
1457                                         lineSet,
1458                                         flag);
1459                 break;
1460         }
1461
1462         case LyXTabular::M_TOGGLE_LINE_RIGHT:
1463                 flag = false;
1464         case LyXTabular::TOGGLE_LINE_RIGHT: {
1465                 bool lineSet = !tabular.rightLine(actcell, flag);
1466                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1467                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1468                                 tabular.setRightLine(
1469                                         tabular.getCellNumber(i,j),
1470                                         lineSet,
1471                                         flag);
1472                 break;
1473         }
1474
1475         case LyXTabular::M_ALIGN_LEFT:
1476         case LyXTabular::M_ALIGN_RIGHT:
1477         case LyXTabular::M_ALIGN_CENTER:
1478                 flag = false;
1479         case LyXTabular::ALIGN_LEFT:
1480         case LyXTabular::ALIGN_RIGHT:
1481         case LyXTabular::ALIGN_CENTER:
1482         case LyXTabular::ALIGN_BLOCK:
1483                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1484                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1485                                 tabular.setAlignment(
1486                                         tabular.getCellNumber(i, j),
1487                                         setAlign,
1488                                         flag);
1489                 break;
1490
1491         case LyXTabular::M_VALIGN_TOP:
1492         case LyXTabular::M_VALIGN_BOTTOM:
1493         case LyXTabular::M_VALIGN_MIDDLE:
1494                 flag = false;
1495         case LyXTabular::VALIGN_TOP:
1496         case LyXTabular::VALIGN_BOTTOM:
1497         case LyXTabular::VALIGN_MIDDLE:
1498                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1499                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1500                                 tabular.setVAlignment(
1501                                         tabular.getCellNumber(i, j),
1502                                         setVAlign, flag);
1503                 break;
1504
1505         case LyXTabular::MULTICOLUMN: {
1506                 if (sel_row_start != sel_row_end) {
1507 #ifdef WITH_WARNINGS
1508 #warning Need I say it ? This is horrible.
1509 #endif
1510                         Alert::error(_("Error setting multicolumn"),
1511                                    _("You cannot set multicolumn vertically."));
1512                         return;
1513                 }
1514                 // just multicol for one Single Cell
1515                 if (!hasSelection()) {
1516                         // check wether we are completly in a multicol
1517                         if (tabular.isMultiColumn(actcell))
1518                                 tabular.unsetMultiColumn(actcell);
1519                         else
1520                                 tabular.setMultiColumn(bv->buffer(), actcell, 1);
1521                         break;
1522                 }
1523                 // we have a selection so this means we just add all this
1524                 // cells to form a multicolumn cell
1525                 int s_start;
1526                 int s_end;
1527
1528                 if (sel_cell_start > sel_cell_end) {
1529                         s_start = sel_cell_end;
1530                         s_end = sel_cell_start;
1531                 } else {
1532                         s_start = sel_cell_start;
1533                         s_end = sel_cell_end;
1534                 }
1535                 tabular.setMultiColumn(bv->buffer(), s_start, s_end - s_start + 1);
1536                 actcell = s_start;
1537                 clearSelection();
1538                 break;
1539         }
1540
1541         case LyXTabular::SET_ALL_LINES:
1542                 setLines = true;
1543         case LyXTabular::UNSET_ALL_LINES:
1544                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1545                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1546                                 tabular.setAllLines(
1547                                         tabular.getCellNumber(i,j), setLines);
1548                 break;
1549
1550         case LyXTabular::SET_LONGTABULAR:
1551                 tabular.setLongTabular(true);
1552                 break;
1553
1554         case LyXTabular::UNSET_LONGTABULAR:
1555                 tabular.setLongTabular(false);
1556                 break;
1557
1558         case LyXTabular::SET_ROTATE_TABULAR:
1559                 tabular.setRotateTabular(true);
1560                 break;
1561
1562         case LyXTabular::UNSET_ROTATE_TABULAR:
1563                 tabular.setRotateTabular(false);
1564                 break;
1565
1566         case LyXTabular::SET_ROTATE_CELL:
1567                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1568                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1569                                 tabular.setRotateCell(
1570                                         tabular.getCellNumber(i, j), true);
1571                 break;
1572
1573         case LyXTabular::UNSET_ROTATE_CELL:
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.setRotateCell(
1577                                         tabular.getCellNumber(i, j), false);
1578                 break;
1579
1580         case LyXTabular::SET_USEBOX: {
1581                 LyXTabular::BoxType val = LyXTabular::BoxType(strToInt(value));
1582                 if (val == tabular.getUsebox(actcell))
1583                         val = LyXTabular::BOX_NONE;
1584                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1585                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1586                                 tabular.setUsebox(tabular.getCellNumber(i, j), val);
1587                 break;
1588         }
1589
1590         case LyXTabular::UNSET_LTFIRSTHEAD:
1591                 flag = false;
1592         case LyXTabular::SET_LTFIRSTHEAD:
1593                 tabular.getRowOfLTFirstHead(row, ltt);
1594                 checkLongtableSpecial(ltt, value, flag);
1595                 tabular.setLTHead(row, flag, ltt, true);
1596                 break;
1597
1598         case LyXTabular::UNSET_LTHEAD:
1599                 flag = false;
1600         case LyXTabular::SET_LTHEAD:
1601                 tabular.getRowOfLTHead(row, ltt);
1602                 checkLongtableSpecial(ltt, value, flag);
1603                 tabular.setLTHead(row, flag, ltt, false);
1604                 break;
1605
1606         case LyXTabular::UNSET_LTFOOT:
1607                 flag = false;
1608         case LyXTabular::SET_LTFOOT:
1609                 tabular.getRowOfLTFoot(row, ltt);
1610                 checkLongtableSpecial(ltt, value, flag);
1611                 tabular.setLTFoot(row, flag, ltt, false);
1612                 break;
1613
1614         case LyXTabular::UNSET_LTLASTFOOT:
1615                 flag = false;
1616         case LyXTabular::SET_LTLASTFOOT:
1617                 tabular.getRowOfLTLastFoot(row, ltt);
1618                 checkLongtableSpecial(ltt, value, flag);
1619                 tabular.setLTFoot(row, flag, ltt, true);
1620                 break;
1621
1622         case LyXTabular::SET_LTNEWPAGE:
1623                 tabular.setLTNewPage(row, !tabular.getLTNewPage(row));
1624                 break;
1625
1626         // dummy stuff just to avoid warnings
1627         case LyXTabular::LAST_ACTION:
1628                 break;
1629         }
1630
1631         updateLocal(bv);
1632         InsetTabularMailer(*this).updateDialog(bv);
1633 }
1634
1635
1636 bool InsetTabular::activateCellInset(BufferView * bv, int x, int y, bool behind)
1637 {
1638         UpdatableInset & inset = tabular.getCellInset(actcell);
1639         if (behind) {
1640 #warning metrics?
1641                 x = inset.x() + inset.width();
1642                 y = inset.descent();
1643         }
1644         inset.edit(bv, x, y);
1645         bv->cursor().push(&inset);
1646         if (!the_locking_inset)
1647                 return false;
1648         updateLocal(bv);
1649         return the_locking_inset;
1650 }
1651
1652
1653 void InsetTabular::deleteLyXText(BufferView * /*bv*/, bool /*recursive*/) const
1654 {
1655         //resizeLyXText(bv, recursive);
1656 }
1657
1658
1659 bool InsetTabular::showInsetDialog(BufferView * bv) const
1660 {
1661         if (!the_locking_inset || !the_locking_inset->showInsetDialog(bv))
1662                 InsetTabularMailer(*this).showDialog(bv);
1663         return true;
1664 }
1665
1666
1667 void InsetTabular::openLayoutDialog(BufferView * bv) const
1668 {
1669 #warning Look here
1670 /*
1671         if (the_locking_inset) {
1672                 InsetTabular * inset = static_cast<InsetTabular *>
1673                         (the_locking_inset->getFirstLockingInsetOfType(TABULAR_CODE));
1674                 if (inset) {
1675                         inset->openLayoutDialog(bv);
1676                         return;
1677                 }
1678         }
1679 */
1680         InsetTabularMailer(*this).showDialog(bv);
1681 }
1682
1683
1684 //
1685 // function returns an object as defined in func_status.h:
1686 // states OK, Unknown, Disabled, On, Off.
1687 //
1688 FuncStatus InsetTabular::getStatus(string const & what) const
1689 {
1690         int action = LyXTabular::LAST_ACTION;
1691         FuncStatus status;
1692
1693         int i = 0;
1694         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
1695                 string const tmp = tabularFeature[i].feature;
1696                 if (tmp == what.substr(0, tmp.length())) {
1697                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
1698                         //   tabularFeatures[i].feature.length())) {
1699                         action = tabularFeature[i].action;
1700                         break;
1701                 }
1702         }
1703         if (action == LyXTabular::LAST_ACTION) {
1704                 status.clear();
1705                 status.unknown(true);
1706                 return status;
1707         }
1708
1709         string const argument
1710                 = ltrim(what.substr(tabularFeature[i].feature.length()));
1711
1712         int sel_row_start;
1713         int sel_row_end;
1714         int dummy;
1715         LyXTabular::ltType dummyltt;
1716         bool flag = true;
1717
1718         if (hasSelection())
1719                 getSelection(sel_row_start, sel_row_end, dummy, dummy);
1720         else
1721                 sel_row_start = sel_row_end = tabular.row_of_cell(actcell);
1722
1723         switch (action) {
1724         case LyXTabular::SET_PWIDTH:
1725         case LyXTabular::SET_MPWIDTH:
1726         case LyXTabular::SET_SPECIAL_COLUMN:
1727         case LyXTabular::SET_SPECIAL_MULTI:
1728         case LyXTabular::APPEND_ROW:
1729         case LyXTabular::APPEND_COLUMN:
1730         case LyXTabular::DELETE_ROW:
1731         case LyXTabular::DELETE_COLUMN:
1732         case LyXTabular::SET_ALL_LINES:
1733         case LyXTabular::UNSET_ALL_LINES:
1734                 status.clear();
1735                 return status;
1736
1737         case LyXTabular::MULTICOLUMN:
1738                 status.setOnOff(tabular.isMultiColumn(actcell));
1739                 break;
1740
1741         case LyXTabular::M_TOGGLE_LINE_TOP:
1742                 flag = false;
1743         case LyXTabular::TOGGLE_LINE_TOP:
1744                 status.setOnOff(tabular.topLine(actcell, flag));
1745                 break;
1746
1747         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
1748                 flag = false;
1749         case LyXTabular::TOGGLE_LINE_BOTTOM:
1750                 status.setOnOff(tabular.bottomLine(actcell, flag));
1751                 break;
1752
1753         case LyXTabular::M_TOGGLE_LINE_LEFT:
1754                 flag = false;
1755         case LyXTabular::TOGGLE_LINE_LEFT:
1756                 status.setOnOff(tabular.leftLine(actcell, flag));
1757                 break;
1758
1759         case LyXTabular::M_TOGGLE_LINE_RIGHT:
1760                 flag = false;
1761         case LyXTabular::TOGGLE_LINE_RIGHT:
1762                 status.setOnOff(tabular.rightLine(actcell, flag));
1763                 break;
1764
1765         case LyXTabular::M_ALIGN_LEFT:
1766                 flag = false;
1767         case LyXTabular::ALIGN_LEFT:
1768                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_LEFT);
1769                 break;
1770
1771         case LyXTabular::M_ALIGN_RIGHT:
1772                 flag = false;
1773         case LyXTabular::ALIGN_RIGHT:
1774                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_RIGHT);
1775                 break;
1776
1777         case LyXTabular::M_ALIGN_CENTER:
1778                 flag = false;
1779         case LyXTabular::ALIGN_CENTER:
1780                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_CENTER);
1781                 break;
1782
1783         case LyXTabular::ALIGN_BLOCK:
1784                 status.disabled(tabular.getPWidth(actcell).zero());
1785                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_BLOCK);
1786                 break;
1787
1788         case LyXTabular::M_VALIGN_TOP:
1789                 flag = false;
1790         case LyXTabular::VALIGN_TOP:
1791                 status.setOnOff(tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_TOP);
1792                 break;
1793
1794         case LyXTabular::M_VALIGN_BOTTOM:
1795                 flag = false;
1796         case LyXTabular::VALIGN_BOTTOM:
1797                 status.setOnOff(tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_BOTTOM);
1798                 break;
1799
1800         case LyXTabular::M_VALIGN_MIDDLE:
1801                 flag = false;
1802         case LyXTabular::VALIGN_MIDDLE:
1803                 status.setOnOff(tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_MIDDLE);
1804                 break;
1805
1806         case LyXTabular::SET_LONGTABULAR:
1807                 status.setOnOff(tabular.isLongTabular());
1808                 break;
1809
1810         case LyXTabular::UNSET_LONGTABULAR:
1811                 status.setOnOff(!tabular.isLongTabular());
1812                 break;
1813
1814         case LyXTabular::SET_ROTATE_TABULAR:
1815                 status.setOnOff(tabular.getRotateTabular());
1816                 break;
1817
1818         case LyXTabular::UNSET_ROTATE_TABULAR:
1819                 status.setOnOff(!tabular.getRotateTabular());
1820                 break;
1821
1822         case LyXTabular::SET_ROTATE_CELL:
1823                 status.setOnOff(tabular.getRotateCell(actcell));
1824                 break;
1825
1826         case LyXTabular::UNSET_ROTATE_CELL:
1827                 status.setOnOff(!tabular.getRotateCell(actcell));
1828                 break;
1829
1830         case LyXTabular::SET_USEBOX:
1831                 status.setOnOff(strToInt(argument) == tabular.getUsebox(actcell));
1832                 break;
1833
1834         case LyXTabular::SET_LTFIRSTHEAD:
1835                 status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
1836                 break;
1837
1838         case LyXTabular::SET_LTHEAD:
1839                 status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
1840                 break;
1841
1842         case LyXTabular::SET_LTFOOT:
1843                 status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
1844                 break;
1845
1846         case LyXTabular::SET_LTLASTFOOT:
1847                 status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
1848                 break;
1849
1850         case LyXTabular::SET_LTNEWPAGE:
1851                 status.setOnOff(tabular.getLTNewPage(sel_row_start));
1852                 break;
1853
1854         default:
1855                 status.clear();
1856                 status.disabled(true);
1857                 break;
1858         }
1859         return status;
1860 }
1861
1862
1863 void InsetTabular::getLabelList(Buffer const & buffer,
1864                                 std::vector<string> & list) const
1865 {
1866         tabular.getLabelList(buffer, list);
1867 }
1868
1869
1870 bool InsetTabular::copySelection(BufferView * bv)
1871 {
1872         if (!hasSelection())
1873                 return false;
1874
1875         int sel_col_start = tabular.column_of_cell(sel_cell_start);
1876         int sel_col_end = tabular.column_of_cell(sel_cell_end);
1877         if (sel_col_start > sel_col_end) {
1878                 sel_col_start = sel_col_end;
1879                 sel_col_end = tabular.right_column_of_cell(sel_cell_start);
1880         } else {
1881                 sel_col_end = tabular.right_column_of_cell(sel_cell_end);
1882         }
1883
1884         int sel_row_start = tabular.row_of_cell(sel_cell_start);
1885         int sel_row_end = tabular.row_of_cell(sel_cell_end);
1886         if (sel_row_start > sel_row_end)
1887                 swap(sel_row_start, sel_row_end);
1888
1889         paste_tabular.reset(new LyXTabular(tabular));
1890         paste_tabular->setOwner(this);
1891
1892         for (int i = 0; i < sel_row_start; ++i)
1893                 paste_tabular->deleteRow(0);
1894
1895         int const rows = sel_row_end - sel_row_start + 1;
1896         while (paste_tabular->rows() > rows)
1897                 paste_tabular->deleteRow(rows);
1898
1899         paste_tabular->setTopLine(0, true, true);
1900         paste_tabular->setBottomLine(paste_tabular->getFirstCellInRow(rows - 1),
1901                                      true, true);
1902
1903         for (int i = 0; i < sel_col_start; ++i)
1904                 paste_tabular->deleteColumn(0);
1905
1906         int const columns = sel_col_end - sel_col_start + 1;
1907         while (paste_tabular->columns() > columns)
1908                 paste_tabular->deleteColumn(columns);
1909
1910         paste_tabular->setLeftLine(0, true, true);
1911         paste_tabular->setRightLine(paste_tabular->getLastCellInRow(0),
1912                                     true, true);
1913
1914         ostringstream os;
1915         OutputParams const runparams;   
1916         paste_tabular->plaintext(*bv->buffer(), os, runparams,
1917                                  ownerPar(*bv->buffer(), this).params().depth(), true, '\t');
1918         bv->stuffClipboard(os.str());
1919         return true;
1920 }
1921
1922
1923 bool InsetTabular::pasteSelection(BufferView * bv)
1924 {
1925         if (!paste_tabular)
1926                 return false;
1927
1928         for (int r1 = 0, r2 = actrow;
1929              r1 < paste_tabular->rows() && r2 < tabular.rows();
1930              ++r1, ++r2) {
1931                 for (int c1 = 0, c2 = actcol;
1932                     c1 < paste_tabular->columns() && c2 < tabular.columns();
1933                     ++c1, ++c2) {
1934                         if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
1935                             tabular.isPartOfMultiColumn(r2, c2))
1936                                 continue;
1937                         if (paste_tabular->isPartOfMultiColumn(r1, c1)) {
1938                                 --c2;
1939                                 continue;
1940                         }
1941                         if (tabular.isPartOfMultiColumn(r2, c2)) {
1942                                 --c1;
1943                                 continue;
1944                         }
1945                         InsetText & inset = tabular.getCellInset(r2, c2);
1946                         inset = paste_tabular->getCellInset(r1, c1);
1947                         inset.setOwner(this);
1948                         inset.deleteLyXText(bv);
1949                         inset.markNew();
1950                 }
1951         }
1952         return true;
1953 }
1954
1955
1956 bool InsetTabular::cutSelection(BufferParams const & bp)
1957 {
1958         if (!hasSelection())
1959                 return false;
1960
1961         int sel_col_start = tabular.column_of_cell(sel_cell_start);
1962         int sel_col_end = tabular.column_of_cell(sel_cell_end);
1963         if (sel_col_start > sel_col_end) {
1964                 sel_col_start = sel_col_end;
1965                 sel_col_end = tabular.right_column_of_cell(sel_cell_start);
1966         } else {
1967                 sel_col_end = tabular.right_column_of_cell(sel_cell_end);
1968         }
1969
1970         int sel_row_start = tabular.row_of_cell(sel_cell_start);
1971         int sel_row_end = tabular.row_of_cell(sel_cell_end);
1972
1973         if (sel_row_start > sel_row_end)
1974                 swap(sel_row_start, sel_row_end);
1975
1976         if (sel_cell_start > sel_cell_end)
1977                 swap(sel_cell_start, sel_cell_end);
1978
1979         for (int i = sel_row_start; i <= sel_row_end; ++i)
1980                 for (int j = sel_col_start; j <= sel_col_end; ++j)
1981                         tabular.getCellInset(tabular.getCellNumber(i, j))
1982                                 .clear(bp.tracking_changes);
1983         return true;
1984 }
1985
1986
1987 bool InsetTabular::isRightToLeft(BufferView * bv)
1988 {
1989         return bv->getParentLanguage(this)->RightToLeft();
1990 }
1991
1992
1993 int InsetTabular::scroll(bool recursive) const
1994 {
1995         int sx = UpdatableInset::scroll(false);
1996
1997         if (recursive && the_locking_inset)
1998                 sx += the_locking_inset->scroll(recursive);
1999
2000         return sx;
2001 }
2002
2003
2004 void InsetTabular::getSelection(int & srow, int & erow,
2005                                 int & scol, int & ecol) const
2006 {
2007         int const start = hasSelection() ? sel_cell_start : actcell;
2008         int const end = hasSelection() ? sel_cell_end : actcell;
2009
2010         srow = tabular.row_of_cell(start);
2011         erow = tabular.row_of_cell(end);
2012         if (srow > erow)
2013                 swap(srow, erow);
2014
2015         scol = tabular.column_of_cell(start);
2016         ecol = tabular.column_of_cell(end);
2017         if (scol > ecol)
2018                 swap(scol, ecol);
2019         else
2020                 ecol = tabular.right_column_of_cell(end);
2021 }
2022
2023
2024 ParagraphList * InsetTabular::getParagraphs(int i) const
2025 {
2026         return i < tabular.getNumberOfCells()
2027                 ? tabular.getCellInset(i).getParagraphs(0)
2028                 : 0;
2029 }
2030
2031
2032 int InsetTabular::numParagraphs() const
2033 {
2034         return tabular.getNumberOfCells();
2035 }
2036
2037
2038 LyXText * InsetTabular::getText(int i) const
2039 {
2040         return i < tabular.getNumberOfCells()
2041                 ? tabular.getCellInset(i).getText(0)
2042                 : 0;
2043 }
2044
2045
2046 void InsetTabular::markErased()
2047 {
2048         for (int cell = 0; cell < tabular.getNumberOfCells(); ++cell)
2049                 tabular.getCellInset(cell).markErased();
2050 }
2051
2052
2053 bool InsetTabular::insetAllowed(InsetOld::Code) const
2054 {
2055         return false;
2056 }
2057
2058
2059 bool InsetTabular::forceDefaultParagraphs(InsetOld const * in) const
2060 {
2061         const int cell = tabular.getCellFromInset(in);
2062
2063         if (cell != -1)
2064                 return tabular.getPWidth(cell).zero();
2065
2066         // this is a workaround for a crash (New, Insert->Tabular,
2067         // Insert->FootNote)
2068         if (!owner())
2069                 return false;
2070
2071         // well we didn't obviously find it so maybe our owner knows more
2072         BOOST_ASSERT(owner());
2073         return owner()->forceDefaultParagraphs(in);
2074 }
2075
2076
2077 bool InsetTabular::insertAsciiString(BufferView * bv, string const & buf,
2078                                      bool usePaste)
2079 {
2080         if (buf.length() <= 0)
2081                 return true;
2082
2083         int cols = 1;
2084         int rows = 1;
2085         int maxCols = 1;
2086         string::size_type len = buf.length();
2087         string::size_type p = 0;
2088
2089         while (p < len && (p = buf.find_first_of("\t\n", p)) != string::npos) {
2090                 switch (buf[p]) {
2091                 case '\t':
2092                         ++cols;
2093                         break;
2094                 case '\n':
2095                         if (p + 1 < len)
2096                                 ++rows;
2097                         maxCols = max(cols, maxCols);
2098                         cols = 1;
2099                         break;
2100                 }
2101                 ++p;
2102         }
2103         maxCols = max(cols, maxCols);
2104         LyXTabular * loctab;
2105         int cell = 0;
2106         int ocol = 0;
2107         int row = 0;
2108         if (usePaste) {
2109                 paste_tabular.reset(
2110                         new LyXTabular(bv->buffer()->params(), rows, maxCols)
2111                         );
2112
2113                 paste_tabular->setOwner(this);
2114                 loctab = paste_tabular.get();
2115                 cols = 0;
2116         } else {
2117                 loctab = &tabular;
2118                 cell = actcell;
2119                 ocol = actcol;
2120                 row = actrow;
2121         }
2122
2123         string::size_type op = 0;
2124         int cells = loctab->getNumberOfCells();
2125         p = 0;
2126         cols = ocol;
2127         rows = loctab->rows();
2128         int const columns = loctab->columns();
2129
2130         while (cell < cells && p < len && row < rows &&
2131                (p = buf.find_first_of("\t\n", p)) != string::npos)
2132         {
2133                 if (p >= len)
2134                         break;
2135                 switch (buf[p]) {
2136                 case '\t':
2137                         // we can only set this if we are not too far right
2138                         if (cols < columns) {
2139                                 InsetText & inset = loctab->getCellInset(cell);
2140                                 LyXFont const font = inset.text_.
2141                                         getFont(inset.paragraphs.begin(), 0);
2142                                 inset.setText(buf.substr(op, p - op), font);
2143                                 ++cols;
2144                                 ++cell;
2145                         }
2146                         break;
2147                 case '\n':
2148                         // we can only set this if we are not too far right
2149                         if (cols < columns) {
2150                                 InsetText & inset = tabular.getCellInset(cell);
2151                                 LyXFont const font = inset.text_.
2152                                         getFont(inset.paragraphs.begin(), 0);
2153                                 inset.setText(buf.substr(op, p - op), font);
2154                         }
2155                         cols = ocol;
2156                         ++row;
2157                         if (row < rows)
2158                                 cell = loctab->getCellNumber(row, cols);
2159                         break;
2160                 }
2161                 ++p;
2162                 op = p;
2163         }
2164         // check for the last cell if there is no trailing '\n'
2165         if (cell < cells && op < len) {
2166                 InsetText & inset = loctab->getCellInset(cell);
2167                 LyXFont const font = inset.text_.getFont(inset.paragraphs.begin(), 0);
2168                 inset.setText(buf.substr(op, len - op), font);
2169         }
2170
2171         return true;
2172 }
2173
2174
2175 void InsetTabular::addPreview(PreviewLoader & loader) const
2176 {
2177         int const rows = tabular.rows();
2178         int const columns = tabular.columns();
2179         for (int i = 0; i < rows; ++i) {
2180                 for (int j = 0; j < columns; ++j)
2181                         tabular.getCellInset(i, j).addPreview(loader);
2182         }
2183 }
2184
2185
2186 string const InsetTabularMailer::name_("tabular");
2187
2188 InsetTabularMailer::InsetTabularMailer(InsetTabular const & inset)
2189         : inset_(const_cast<InsetTabular &>(inset))
2190 {}
2191
2192
2193 string const InsetTabularMailer::inset2string(Buffer const &) const
2194 {
2195         return params2string(inset_);
2196 }
2197
2198
2199 int InsetTabularMailer::string2params(string const & in, InsetTabular & inset)
2200 {
2201         istringstream data(in);
2202         LyXLex lex(0,0);
2203         lex.setStream(data);
2204
2205 #warning CHECK verify that this is a sane value to return.
2206         if (in.empty())
2207                 return -1;
2208
2209         if (lex.isOK()) {
2210                 lex.next();
2211                 string const token = lex.getString();
2212                 if (token != name_)
2213                         return -1;
2214         }
2215
2216         int cell = -1;
2217         if (lex.isOK()) {
2218                 lex.next();
2219                 string const token = lex.getString();
2220                 if (token != "\\active_cell")
2221                         return -1;
2222                 lex.next();
2223                 cell = lex.getInteger();
2224         }
2225
2226         // This is part of the inset proper that is usually swallowed
2227         // by Buffer::readInset
2228         if (lex.isOK()) {
2229                 lex.next();
2230                 string const token = lex.getString();
2231                 if (token != "Tabular")
2232                         return -1;
2233         }
2234
2235         if (!lex.isOK())
2236                 return -1;
2237
2238         Buffer const & buffer = inset.buffer();
2239         inset.read(buffer, lex);
2240
2241         // We can't set the active cell, but we can tell the frontend
2242         // what it is.
2243         return cell;
2244 }
2245
2246
2247 string const InsetTabularMailer::params2string(InsetTabular const & inset)
2248 {
2249         ostringstream data;
2250         data << name_ << " \\active_cell " << inset.getActCell() << '\n';
2251         inset.write(inset.buffer(), data);
2252         data << "\\end_inset\n";
2253         return data.str();
2254 }