]> git.lyx.org Git - features.git/blob - src/insets/insettabular.C
remove InsetUpdatable::nodraw (premature...) optimization
[features.git] / src / insets / insettabular.C
1 /**
2  * \file insettabular.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  *
8  * Full author contact details are available in file CREDITS
9  */
10
11 #include <config.h>
12
13 #include "insettabular.h"
14 #include "insettext.h"
15
16 #include "buffer.h"
17 #include "BufferView.h"
18 #include "lfuns.h"
19 #include "debug.h"
20 #include "dimension.h"
21 #include "funcrequest.h"
22 #include "gettext.h"
23 #include "language.h"
24 #include "LaTeXFeatures.h"
25 #include "Lsstream.h"
26 #include "lyx_cb.h"
27 #include "lyxfunc.h"
28 #include "lyxlength.h"
29 #include "lyxlex.h"
30 #include "lyxtext.h"
31 #include "ParagraphParameters.h"
32 #include "undo_funcs.h"
33 #include "WordLangTuple.h"
34 #include "metricsinfo.h"
35
36 #include "frontends/Alert.h"
37 #include "frontends/Dialogs.h"
38 #include "frontends/font_metrics.h"
39 #include "frontends/LyXView.h"
40 #include "frontends/Painter.h"
41
42 #include "support/LAssert.h"
43 #include "support/lstrings.h"
44
45 #include <fstream>
46 #include <algorithm>
47 #include <cstdlib>
48 #include <map>
49
50
51 using namespace lyx::support;
52 using namespace lyx::graphics;
53
54 using std::vector;
55 using std::ostream;
56 using std::ifstream;
57 using std::max;
58 using std::endl;
59 using std::swap;
60 using std::max;
61 using std::auto_ptr;
62
63
64 namespace {
65
66 int const ADD_TO_HEIGHT = 2;
67 int const ADD_TO_TABULAR_WIDTH = 2;
68
69 ///
70 LyXTabular * paste_tabular = 0;
71
72
73 struct TabularFeature {
74         LyXTabular::Feature action;
75         string feature;
76 };
77
78
79 TabularFeature tabularFeature[] =
80 {
81         { LyXTabular::APPEND_ROW, "append-row" },
82         { LyXTabular::APPEND_COLUMN, "append-column" },
83         { LyXTabular::DELETE_ROW, "delete-row" },
84         { LyXTabular::DELETE_COLUMN, "delete-column" },
85         { LyXTabular::TOGGLE_LINE_TOP, "toggle-line-top" },
86         { LyXTabular::TOGGLE_LINE_BOTTOM, "toggle-line-bottom" },
87         { LyXTabular::TOGGLE_LINE_LEFT, "toggle-line-left" },
88         { LyXTabular::TOGGLE_LINE_RIGHT, "toggle-line-right" },
89         { LyXTabular::ALIGN_LEFT, "align-left" },
90         { LyXTabular::ALIGN_RIGHT, "align-right" },
91         { LyXTabular::ALIGN_CENTER, "align-center" },
92         { LyXTabular::ALIGN_BLOCK, "align-block" },
93         { LyXTabular::VALIGN_TOP, "valign-top" },
94         { LyXTabular::VALIGN_BOTTOM, "valign-bottom" },
95         { LyXTabular::VALIGN_CENTER, "valign-center" },
96         { LyXTabular::M_TOGGLE_LINE_TOP, "m-toggle-line-top" },
97         { LyXTabular::M_TOGGLE_LINE_BOTTOM, "m-toggle-line-bottom" },
98         { LyXTabular::M_TOGGLE_LINE_LEFT, "m-toggle-line-left" },
99         { LyXTabular::M_TOGGLE_LINE_RIGHT, "m-toggle-line-right" },
100         { LyXTabular::M_ALIGN_LEFT, "m-align-left" },
101         { LyXTabular::M_ALIGN_RIGHT, "m-align-right" },
102         { LyXTabular::M_ALIGN_CENTER, "m-align-center" },
103         { LyXTabular::M_VALIGN_TOP, "m-valign-top" },
104         { LyXTabular::M_VALIGN_BOTTOM, "m-valign-bottom" },
105         { LyXTabular::M_VALIGN_CENTER, "m-valign-center" },
106         { LyXTabular::MULTICOLUMN, "multicolumn" },
107         { LyXTabular::SET_ALL_LINES, "set-all-lines" },
108         { LyXTabular::UNSET_ALL_LINES, "unset-all-lines" },
109         { LyXTabular::SET_LONGTABULAR, "set-longtabular" },
110         { LyXTabular::UNSET_LONGTABULAR, "unset-longtabular" },
111         { LyXTabular::SET_PWIDTH, "set-pwidth" },
112         { LyXTabular::SET_MPWIDTH, "set-mpwidth" },
113         { LyXTabular::SET_ROTATE_TABULAR, "set-rotate-tabular" },
114         { LyXTabular::UNSET_ROTATE_TABULAR, "unset-rotate-tabular" },
115         { LyXTabular::SET_ROTATE_CELL, "set-rotate-cell" },
116         { LyXTabular::UNSET_ROTATE_CELL, "unset-rotate-cell" },
117         { LyXTabular::SET_USEBOX, "set-usebox" },
118         { LyXTabular::SET_LTHEAD, "set-lthead" },
119         { LyXTabular::SET_LTFIRSTHEAD, "set-ltfirsthead" },
120         { LyXTabular::SET_LTFOOT, "set-ltfoot" },
121         { LyXTabular::SET_LTLASTFOOT, "set-ltlastfoot" },
122         { LyXTabular::SET_LTNEWPAGE, "set-ltnewpage" },
123         { LyXTabular::SET_SPECIAL_COLUMN, "set-special-column" },
124         { LyXTabular::SET_SPECIAL_MULTI, "set-special-multi" },
125         { LyXTabular::LAST_ACTION, "" }
126 };
127
128 struct FindFeature {
129         FindFeature(LyXTabular::Feature feature) : feature_(feature) {}
130         bool operator()(TabularFeature & tf)
131         {
132                 return tf.action == feature_;
133         }
134 private:
135         LyXTabular::Feature feature_;
136 };
137
138 } // namespace anon
139
140
141 string const featureAsString(LyXTabular::Feature feature)
142 {
143         TabularFeature * it  = tabularFeature;
144         TabularFeature * end = it +
145                 sizeof(tabularFeature) / sizeof(TabularFeature);
146         it = std::find_if(it, end, FindFeature(feature));
147         return (it == end) ? string() : it->feature;
148 }
149
150
151 bool InsetTabular::hasPasteBuffer() const
152 {
153         return (paste_tabular != 0);
154 }
155
156
157 InsetTabular::InsetTabular(Buffer const & buf, int rows, int columns)
158         : tabular(buf.params, this, max(rows, 1), max(columns, 1)),
159           buffer_(&buf), cursorx_(0), cursory_(0)
160 {
161         // for now make it always display as display() inset
162         // just for test!!!
163         the_locking_inset = 0;
164         old_locking_inset = 0;
165         locked = false;
166         oldcell = -1;
167         actrow = actcell = 0;
168         clearSelection();
169         need_update = INIT;
170         in_reset_pos = 0;
171         inset_x = 0;
172         inset_y = 0;
173 }
174
175
176 InsetTabular::InsetTabular(InsetTabular const & tab)
177         : UpdatableInset(tab),
178                 tabular(tab.buffer_->params, this, tab.tabular),
179                 buffer_(tab.buffer_), cursorx_(0), cursory_(0)
180 {
181         the_locking_inset = 0;
182         old_locking_inset = 0;
183         locked = false;
184         oldcell = -1;
185         actrow = actcell = 0;
186         clearSelection();
187         need_update = INIT;
188         in_reset_pos = 0;
189         inset_x = 0;
190         inset_y = 0;
191 }
192
193
194 InsetTabular::~InsetTabular()
195 {
196         InsetTabularMailer(*this).hideDialog();
197 }
198
199
200 auto_ptr<InsetBase> InsetTabular::clone() const
201 {
202         return auto_ptr<InsetBase>(new InsetTabular(*this));
203 }
204
205
206 Buffer const * InsetTabular::buffer() const
207 {
208         return buffer_;
209 }
210
211
212 BufferView * InsetTabular::view() const
213 {
214         Assert(false);
215         return 0;
216 }
217
218
219 void InsetTabular::buffer(Buffer * b)
220 {
221         buffer_ = b;
222 }
223
224
225 void InsetTabular::write(Buffer const * buf, ostream & os) const
226 {
227         os << "Tabular" << endl;
228         tabular.write(buf, os);
229 }
230
231
232 void InsetTabular::read(Buffer const * buf, LyXLex & lex)
233 {
234         bool const old_format = (lex.getString() == "\\LyXTable");
235
236         tabular.read(buf, lex);
237
238         need_update = INIT;
239
240         if (old_format)
241                 return;
242
243         lex.nextToken();
244         string token = lex.getString();
245         while (lex.isOK() && (token != "\\end_inset")) {
246                 lex.nextToken();
247                 token = lex.getString();
248         }
249         if (token != "\\end_inset") {
250                 lex.printError("Missing \\end_inset at this point. "
251                                "Read: `$$Token'");
252         }
253 }
254
255
256 void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
257 {
258         //lyxerr << "InsetTabular::metrics: " << mi.base.bv << " width: " <<
259         //      mi.base.textwidth << "\n";
260         if (!mi.base.bv) {
261                 lyxerr << "InsetTabular::metrics: need bv\n";
262                 Assert(0);
263         }
264
265         calculate_dimensions_of_cells(mi);
266
267         dim.asc = tabular.getAscentOfRow(0);
268         dim.des = tabular.getHeightOfTabular() - tabular.getAscentOfRow(0) + 1;
269         dim.wid = tabular.getWidthOfTabular() + 2 * ADD_TO_TABULAR_WIDTH;
270         dim_ = dim;
271 }
272
273
274 void InsetTabular::draw(PainterInfo & pi, int x, int y) const
275 {
276         //lyxerr << "InsetTabular::draw: " << x << " " << y << "\n";
277
278         BufferView * bv = pi.base.bv;
279
280 #if 0
281         UpdatableInset::draw(pi, x, y);
282 #else
283         if (!owner())
284                 x += scroll();
285 #endif
286
287         top_x = x;
288         top_baseline = y;
289         x += ADD_TO_TABULAR_WIDTH;
290
291         int cell = 0;
292         first_visible_cell = -1;
293         for (int i = 0; i < tabular.rows(); ++i) {
294                 int nx = x;
295                 cell = tabular.getCellNumber(i, 0);
296                 if (y + tabular.getDescentOfRow(i) <= 0 &&
297                           y - tabular.getAscentOfRow(i) < pi.pain.paperHeight())
298                 {
299                         y += tabular.getDescentOfRow(i) +
300                                         tabular.getAscentOfRow(i + 1) +
301                                         tabular.getAdditionalHeight(i + 1);
302                         continue;
303                 }
304                 for (int j = 0; j < tabular.columns(); ++j) {
305                         if (nx > bv->workWidth())
306                                 break;
307                         if (tabular.isPartOfMultiColumn(i, j))
308                                 continue;
309                         int cx = nx + tabular.getBeginningOfTextInCell(cell);
310                         if (first_visible_cell < 0)
311                                 first_visible_cell = cell;
312                         if (hasSelection()) {
313                                 drawCellSelection(pi.pain, nx, y, i, j, cell);
314                         }
315
316                         tabular.getCellInset(cell).draw(pi, cx, y);
317                         drawCellLines(pi.pain, nx, y, i, cell);
318                         nx += tabular.getWidthOfColumn(cell);
319                         ++cell;
320                 }
321
322 // Would be nice, but for some completely unfathomable reason,
323 // on a col resize to a new fixed width, even though the insettexts
324 // are resized, the cell isn't, but drawing all cells in a tall table
325 // has the desired effect somehow. Complete dark magic.
326 #if 0
327                 // avoiding drawing the rest of a long table is
328                 // a pretty big speedup
329                 if (y > bv->workHeight())
330                         break;
331 #endif
332
333                 y += tabular.getDescentOfRow(i) +
334                         tabular.getAscentOfRow(i + 1) +
335                         tabular.getAdditionalHeight(i + 1);
336         }
337
338         need_update = NONE;
339 }
340
341
342 void InsetTabular::drawCellLines(Painter & pain, int x, int y,
343                                  int row, int cell) const
344 {
345         int x2 = x + tabular.getWidthOfColumn(cell);
346         bool on_off;
347
348         if (!tabular.topAlreadyDrawn(cell)) {
349                 on_off = !tabular.topLine(cell);
350                 pain.line(x, y - tabular.getAscentOfRow(row),
351                           x2, y -  tabular.getAscentOfRow(row),
352                           on_off ? LColor::tabularonoffline : LColor::tabularline,
353                           on_off ? Painter::line_onoffdash : Painter::line_solid);
354         }
355         on_off = !tabular.bottomLine(cell);
356         pain.line(x, y + tabular.getDescentOfRow(row),
357                   x2, y + tabular.getDescentOfRow(row),
358                   on_off ? LColor::tabularonoffline : LColor::tabularline,
359                   on_off ? Painter::line_onoffdash : Painter::line_solid);
360         if (!tabular.leftAlreadyDrawn(cell)) {
361                 on_off = !tabular.leftLine(cell);
362                 pain.line(x, y -  tabular.getAscentOfRow(row),
363                           x, y +  tabular.getDescentOfRow(row),
364                           on_off ? LColor::tabularonoffline : LColor::tabularline,
365                           on_off ? Painter::line_onoffdash : Painter::line_solid);
366         }
367         on_off = !tabular.rightLine(cell);
368         pain.line(x2 - tabular.getAdditionalWidth(cell),
369                   y -  tabular.getAscentOfRow(row),
370                   x2 - tabular.getAdditionalWidth(cell),
371                   y +  tabular.getDescentOfRow(row),
372                   on_off ? LColor::tabularonoffline : LColor::tabularline,
373                   on_off ? Painter::line_onoffdash : Painter::line_solid);
374 }
375
376
377 void InsetTabular::drawCellSelection(Painter & pain, int x, int y,
378                                      int row, int column, int cell) const
379 {
380         Assert(hasSelection());
381         int cs = tabular.column_of_cell(sel_cell_start);
382         int ce = tabular.column_of_cell(sel_cell_end);
383         if (cs > ce) {
384                 ce = cs;
385                 cs = tabular.column_of_cell(sel_cell_end);
386         } else {
387                 ce = tabular.right_column_of_cell(sel_cell_end);
388         }
389
390         int rs = tabular.row_of_cell(sel_cell_start);
391         int re = tabular.row_of_cell(sel_cell_end);
392         if (rs > re)
393                 swap(rs, re);
394
395         if ((column >= cs) && (column <= ce) && (row >= rs) && (row <= re)) {
396                 int w = tabular.getWidthOfColumn(cell);
397                 int h = tabular.getAscentOfRow(row) + tabular.getDescentOfRow(row)-1;
398                 pain.fillRectangle(x, y - tabular.getAscentOfRow(row) + 1,
399                                    w, h, LColor::selection);
400         }
401 }
402
403
404 string const InsetTabular::editMessage() const
405 {
406         return _("Opened table");
407 }
408
409
410 void InsetTabular::insetUnlock(BufferView * bv)
411 {
412         if (the_locking_inset) {
413                 the_locking_inset->insetUnlock(bv);
414                 updateLocal(bv, CELL);
415                 the_locking_inset = 0;
416         }
417         actcell = 0;
418         oldcell = -1;
419         locked = false;
420         if (scroll(false) || hasSelection()) {
421                 clearSelection();
422                 if (scroll(false)) {
423                         scroll(bv, 0.0F);
424                 }
425                 updateLocal(bv, FULL);
426         }
427 }
428
429
430 void InsetTabular::updateLocal(BufferView * bv, UpdateCodes what) const
431 {
432         lyxerr << "InsetTabular::updateLocal: " << what << "\n";
433         if (!locked && what == CELL)
434                 what = FULL;
435         if (need_update < what) // only set this if it has greater update
436                 need_update = what;
437         // Dirty Cast! (Lgb)
438         if (need_update != NONE) {
439                 bv->updateInset(const_cast<InsetTabular *>(this));
440                 if (locked)
441                         resetPos(bv);
442         }
443 }
444
445
446 bool InsetTabular::lockInsetInInset(BufferView * bv, UpdatableInset * inset)
447 {
448         lyxerr[Debug::INSETTEXT] << "InsetTabular::LockInsetInInset("
449                               << inset << "): ";
450         if (!inset)
451                 return false;
452         oldcell = -1;
453         if (inset == &tabular.getCellInset(actcell)) {
454                 lyxerr[Debug::INSETTEXT] << "OK" << endl;
455                 the_locking_inset = &tabular.getCellInset(actcell);
456                 resetPos(bv);
457                 return true;
458         } else if (!the_locking_inset) {
459                 int const n = tabular.getNumberOfCells();
460                 int const id = inset->id();
461                 for (int i = 0; i < n; ++i) {
462                         InsetText * in = &tabular.getCellInset(i);
463                         if (inset == in) {
464                                 actcell = i;
465                                 the_locking_inset = in;
466                                 locked = true;
467                                 resetPos(bv);
468                                 return true;
469                         }
470                         if (in->getInsetFromID(id)) {
471                                 actcell = i;
472                                 in->localDispatch(FuncRequest(bv, LFUN_INSET_EDIT));
473                                 return the_locking_inset->lockInsetInInset(bv, inset);
474                         }
475                 }
476         } else if (the_locking_inset && (the_locking_inset == inset)) {
477                 lyxerr[Debug::INSETTEXT] << "OK" << endl;
478                 resetPos(bv);
479         } else if (the_locking_inset) {
480                 lyxerr[Debug::INSETTEXT] << "MAYBE" << endl;
481                 return the_locking_inset->lockInsetInInset(bv, inset);
482         }
483         lyxerr[Debug::INSETTEXT] << "NOT OK" << endl;
484         return false;
485 }
486
487
488 bool InsetTabular::unlockInsetInInset(BufferView * bv, UpdatableInset * inset,
489                                       bool lr)
490 {
491         if (!the_locking_inset)
492                 return false;
493         if (the_locking_inset == inset) {
494                 the_locking_inset->insetUnlock(bv);
495 #ifdef WITH_WARNINGS
496 #warning fix scrolling when cellinset has requested a scroll (Jug)!!!
497 #endif
498 #if 0
499                 if (scroll(false))
500                         scroll(bv, 0.0F);
501 #endif
502                 updateLocal(bv, CELL);
503                 // this has to be here otherwise we don't redraw the cell!
504                 the_locking_inset = 0;
505                 return true;
506         }
507         if (the_locking_inset->unlockInsetInInset(bv, inset, lr)) {
508                 if (inset->lyxCode() == TABULAR_CODE &&
509                     !the_locking_inset->getFirstLockingInsetOfType(TABULAR_CODE)) {
510                         InsetTabularMailer(*this).updateDialog(bv);
511                         oldcell = actcell;
512                 }
513                 return true;
514         }
515         return false;
516 }
517
518
519 bool InsetTabular::updateInsetInInset(BufferView * bv, InsetOld * inset)
520 {
521         InsetOld * tl_inset = inset;
522         // look if this inset is really inside myself!
523         while(tl_inset->owner() && tl_inset->owner() != this)
524                 tl_inset = tl_inset->owner();
525         // if we enter here it's not ower inset
526         if (!tl_inset->owner())
527                 return false;
528         // we only have to do this if this is a subinset of our cells
529         if (tl_inset != inset) {
530                 if (!static_cast<InsetText *>(tl_inset)->updateInsetInInset(bv, inset))
531                         return false;
532         }
533         updateLocal(bv, CELL);
534         return true;
535 }
536
537
538 int InsetTabular::insetInInsetY() const
539 {
540         if (!the_locking_inset)
541                 return 0;
542         return inset_y + the_locking_inset->insetInInsetY();
543 }
544
545
546 UpdatableInset * InsetTabular::getLockingInset() const
547 {
548         return the_locking_inset ? the_locking_inset->getLockingInset() :
549                 const_cast<InsetTabular *>(this);
550 }
551
552
553 UpdatableInset * InsetTabular::getFirstLockingInsetOfType(InsetOld::Code c)
554 {
555         if (c == lyxCode())
556                 return this;
557         if (the_locking_inset)
558                 return the_locking_inset->getFirstLockingInsetOfType(c);
559         return 0;
560 }
561
562
563 bool InsetTabular::insertInset(BufferView * bv, InsetOld * inset)
564 {
565         if (the_locking_inset)
566                 return the_locking_inset->insertInset(bv, inset);
567         return false;
568 }
569
570
571 void InsetTabular::lfunMousePress(FuncRequest const & cmd)
572 {
573         if (hasSelection() && cmd.button() == mouse_button::button3)
574                 return;
575
576         if (hasSelection()) {
577                 clearSelection();
578                 updateLocal(cmd.view(), FULL);
579         }
580
581         int const ocell = actcell;
582         int const orow = actrow;
583         BufferView * bv = cmd.view();
584
585         if (!locked) {
586                 locked = true;
587                 the_locking_inset = 0;
588                 inset_x = 0;
589                 inset_y = 0;
590         }
591         setPos(bv, cmd.x, cmd.y);
592         if (actrow != orow)
593                 updateLocal(bv, NONE);
594         clearSelection();
595 #if 0
596         if (cmd.button() == mouse_button::button3) {
597                 if ((ocell != actcell) && the_locking_inset) {
598                         the_locking_inset->insetUnlock(bv);
599                         updateLocal(bv, CELL);
600                         the_locking_inset = 0;
601                 }
602                 return;
603         }
604 #endif
605
606         bool const inset_hit = insetHit(bv, cmd.x, cmd.y);
607
608         if ((ocell == actcell) && the_locking_inset && inset_hit) {
609                 resetPos(bv);
610                 FuncRequest cmd1 = cmd;
611                 cmd1.x -= inset_x;
612                 cmd1.y -= inset_y;
613                 the_locking_inset->localDispatch(cmd1);
614                 return;
615         }
616
617         if (the_locking_inset) {
618                 the_locking_inset->insetUnlock(bv);
619                 updateLocal(bv, CELL);
620                 the_locking_inset = 0;
621         }
622
623         if (cmd.button() == mouse_button::button2) {
624                 localDispatch(FuncRequest(bv, LFUN_PASTESELECTION, "paragraph"));
625                 return;
626         }
627
628         if (inset_hit && bv->theLockingInset()) {
629                 if (!bv->lockInset(&tabular.getCellInset(actcell))) {
630                         lyxerr[Debug::INSETS] << "Cannot lock inset" << endl;
631                         return;
632                 }
633                 FuncRequest cmd1 = cmd;
634                 cmd1.x -= inset_x;
635                 cmd1.y -= inset_y;
636                 the_locking_inset->localDispatch(cmd1);
637                 return;
638         }
639 }
640
641
642 bool InsetTabular::lfunMouseRelease(FuncRequest const & cmd)
643 {
644         bool ret = false;
645         if (the_locking_inset) {
646                 FuncRequest cmd1 = cmd;
647                 cmd1.x -= inset_x;
648                 cmd1.y -= inset_y;
649                 ret = the_locking_inset->localDispatch(cmd1);
650         }
651         if (cmd.button() == mouse_button::button3 && !ret) {
652                 InsetTabularMailer(*this).showDialog(cmd.view());
653                 return true;
654         }
655         return ret;
656 }
657
658
659 void InsetTabular::lfunMouseMotion(FuncRequest const & cmd)
660 {
661         if (the_locking_inset) {
662                 FuncRequest cmd1 = cmd;
663                 cmd1.x -= inset_x;
664                 cmd1.y -= inset_y;
665                 the_locking_inset->localDispatch(cmd1);
666                 return;
667         }
668
669         BufferView * bv = cmd.view();
670         int const old_cell = actcell;
671
672         setPos(bv, cmd.x, cmd.y);
673         if (!hasSelection()) {
674                 setSelection(actcell, actcell);
675                 updateLocal(bv, FULL);
676         } else if (old_cell != actcell) {
677                 setSelection(sel_cell_start, actcell);
678                 updateLocal(bv, FULL);
679         }
680 }
681
682
683 InsetOld::RESULT InsetTabular::localDispatch(FuncRequest const & cmd)
684 {
685         // We need to save the value of the_locking_inset as the call to
686         // the_locking_inset->localDispatch might unlock it.
687         old_locking_inset = the_locking_inset;
688         RESULT result = UpdatableInset::localDispatch(cmd);
689         BufferView * bv = cmd.view();
690
691         if (cmd.action == LFUN_INSET_EDIT) {
692
693                 if (!bv->lockInset(this)) {
694                         lyxerr[Debug::INSETTEXT] << "InsetTabular::Cannot lock inset" << endl;
695                         return DISPATCHED;
696                 }
697
698                 finishUndo();
699                 locked = true;
700                 the_locking_inset = 0;
701                 inset_x = 0;
702                 inset_y = 0;
703
704                 if (cmd.argument.size()) {
705                         if (cmd.argument == "left") {
706                                 if (isRightToLeft(bv))
707                                         actcell = tabular.getLastCellInRow(0);
708                                 else
709                                         actcell = 0;
710                         } else {
711                                 if (isRightToLeft(bv))
712                                         actcell = tabular.getFirstCellInRow(tabular.rows()-1);
713                                 else
714                                         actcell = tabular.getNumberOfCells() - 1;
715                         }
716                         clearSelection();
717                         resetPos(bv);
718                         bv->fitCursor();
719                 }
720
721                 else {
722                         setPos(bv, cmd.x, cmd.y);
723                         clearSelection();
724                         finishUndo();
725                         if (insetHit(bv, cmd.x, cmd.y) && cmd.button() != mouse_button::button3) {
726                                 activateCellInsetAbs(bv, cmd.x, cmd.y, cmd.button());
727                         }
728                 }
729                 return DISPATCHED;
730         }
731
732         if (result == DISPATCHED || result == DISPATCHED_NOUPDATE) {
733                 resetPos(bv);
734                 return result;
735         }
736
737         if (cmd.action < 0 && cmd.argument.empty())
738                 return FINISHED;
739
740         bool hs = hasSelection();
741
742         result = DISPATCHED;
743         // this one have priority over the locked InsetText, if we're not already
744         // inside another tabular then that one get's priority!
745         if (getFirstLockingInsetOfType(InsetOld::TABULAR_CODE) == this) {
746                 switch (cmd.action) {
747                 case LFUN_MOUSE_PRESS:
748                         lfunMousePress(cmd);
749                         return DISPATCHED;
750
751                 case LFUN_MOUSE_MOTION:
752                         lfunMouseMotion(cmd);
753                         return DISPATCHED;
754
755                 case LFUN_MOUSE_RELEASE:
756                         return lfunMouseRelease(cmd) ? DISPATCHED : UNDISPATCHED;
757
758                 case LFUN_CELL_BACKWARD:
759                 case LFUN_CELL_FORWARD:
760                         unlockInsetInInset(bv, the_locking_inset);
761                         if (cmd.action == LFUN_CELL_FORWARD)
762                                 moveNextCell(bv, old_locking_inset != 0);
763                         else
764                                 movePrevCell(bv, old_locking_inset != 0);
765                         clearSelection();
766                         if (hs)
767                                 updateLocal(bv, FULL);
768                         if (!the_locking_inset) {
769                                 return DISPATCHED_NOUPDATE;
770                         }
771                         return result;
772                 // this to avoid compiler warnings.
773                 default:
774                         break;
775                 }
776         }
777
778         kb_action action = cmd.action;
779         string    arg    = cmd.argument;
780         if (the_locking_inset) {
781                 result = the_locking_inset->localDispatch(cmd);
782                 if (result == DISPATCHED_NOUPDATE) {
783                         int sc = scroll();
784                         resetPos(bv);
785                         if (sc != scroll()) { // inset has been scrolled
786                                 updateLocal(bv, FULL);
787                         }
788                         return result;
789                 } else if (result == DISPATCHED) {
790                         updateLocal(bv, CELL);
791                         return result;
792                 } else if (result == FINISHED_UP) {
793                         action = LFUN_UP;
794                         // Make sure to reset status message after
795                         // exiting, e.g. math inset
796                         bv->owner()->clearMessage();
797                 } else if (result == FINISHED_DOWN) {
798                         action = LFUN_DOWN;
799                         bv->owner()->clearMessage();
800                 } else if (result == FINISHED_RIGHT) {
801                         action = LFUN_RIGHT;
802                         bv->owner()->clearMessage();
803                 } else if (result == FINISHED) {
804                         bv->owner()->clearMessage();
805                 }
806         }
807
808         result = DISPATCHED;
809         switch (action) {
810                 // --- Cursor Movements ----------------------------------
811         case LFUN_RIGHTSEL: {
812                 int const start = hasSelection() ? sel_cell_start : actcell;
813                 if (tabular.isLastCellInRow(actcell)) {
814                         setSelection(start, actcell);
815                         break;
816                 }
817
818                 int end = actcell;
819                 // if we are starting a selection, only select
820                 // the current cell at the beginning
821                 if (hasSelection()) {
822                         moveRight(bv, false);
823                         end = actcell;
824                 }
825                 setSelection(start, end);
826                 updateLocal(bv, FULL);
827                 break;
828         }
829         case LFUN_RIGHT:
830                 result = moveRight(bv);
831                 clearSelection();
832                 if (hs)
833                         updateLocal(bv, FULL);
834                 break;
835         case LFUN_LEFTSEL: {
836                 int const start = hasSelection() ? sel_cell_start : actcell;
837                 if (tabular.isFirstCellInRow(actcell)) {
838                         setSelection(start, actcell);
839                         break;
840                 }
841
842                 int end = actcell;
843                 // if we are starting a selection, only select
844                 // the current cell at the beginning
845                 if (hasSelection()) {
846                         moveLeft(bv, false);
847                         end = actcell;
848                 }
849                 setSelection(start, end);
850                 updateLocal(bv, FULL);
851                 break;
852         }
853         case LFUN_LEFT:
854                 result = moveLeft(bv);
855                 clearSelection();
856                 if (hs)
857                         updateLocal(bv, FULL);
858                 break;
859         case LFUN_DOWNSEL: {
860                 int const start = hasSelection() ? sel_cell_start : actcell;
861                 int const ocell = actcell;
862                 // if we are starting a selection, only select
863                 // the current cell at the beginning
864                 if (hasSelection()) {
865                         moveDown(bv, false);
866                         if ((ocell == sel_cell_end) ||
867                             (tabular.column_of_cell(ocell)>tabular.column_of_cell(actcell)))
868                                 setSelection(start, tabular.getCellBelow(sel_cell_end));
869                         else
870                                 setSelection(start, tabular.getLastCellBelow(sel_cell_end));
871                 } else {
872                         setSelection(start, start);
873                 }
874                 updateLocal(bv, FULL);
875         }
876         break;
877         case LFUN_DOWN:
878                 result = moveDown(bv, old_locking_inset != 0);
879                 clearSelection();
880                 if (hs) {
881                         updateLocal(bv, FULL);
882                 }
883                 break;
884         case LFUN_UPSEL: {
885                 int const start = hasSelection() ? sel_cell_start : actcell;
886                 int const ocell = actcell;
887                 // if we are starting a selection, only select
888                 // the current cell at the beginning
889                 if (hasSelection()) {
890                         moveUp(bv, false);
891                         if ((ocell == sel_cell_end) ||
892                             (tabular.column_of_cell(ocell)>tabular.column_of_cell(actcell)))
893                                 setSelection(start, tabular.getCellAbove(sel_cell_end));
894                         else
895                                 setSelection(start, tabular.getLastCellAbove(sel_cell_end));
896                 } else {
897                         setSelection(start, start);
898                 }
899                 updateLocal(bv, FULL);
900         }
901         break;
902         case LFUN_UP:
903                 result = moveUp(bv, old_locking_inset != 0);
904                 clearSelection();
905                 if (hs)
906                         updateLocal(bv, FULL);
907                 break;
908         case LFUN_NEXT: {
909                 UpdateCodes code = CURSOR;
910                 if (hs) {
911                         clearSelection();
912                         code = FULL;
913                 }
914                 int column = actcol;
915                 unlockInsetInInset(bv, the_locking_inset);
916                 if (bv->text->top_y() + bv->painter().paperHeight() <
917                     (top_baseline + tabular.getHeightOfTabular()))
918                         {
919                                 bv->scrollDocView(bv->text->top_y() + bv->painter().paperHeight());
920                                 code = FULL;
921                                 actcell = tabular.getCellBelow(first_visible_cell) + column;
922                         } else {
923                                 actcell = tabular.getFirstCellInRow(tabular.rows() - 1) + column;
924                         }
925                 resetPos(bv);
926                 updateLocal(bv, code);
927                 break;
928         }
929         case LFUN_PRIOR: {
930                 UpdateCodes code = CURSOR;
931                 if (hs) {
932                         clearSelection();
933                         code = FULL;
934                 }
935                 int column = actcol;
936                 unlockInsetInInset(bv, the_locking_inset);
937                 if (top_baseline < 0) {
938                         bv->scrollDocView(bv->text->top_y() - bv->painter().paperHeight());
939                         code = FULL;
940                         if (top_baseline > 0)
941                                 actcell = column;
942                         else
943                                 actcell = tabular.getCellBelow(first_visible_cell) + column;
944                 } else {
945                         actcell = column;
946                 }
947                 resetPos(bv);
948                 updateLocal(bv, code);
949                 break;
950         }
951         // none of these make sense for insettabular,
952         // but we must catch them to prevent any
953         // selection from being confused
954         case LFUN_PRIORSEL:
955         case LFUN_NEXTSEL:
956         case LFUN_WORDLEFT:
957         case LFUN_WORDLEFTSEL:
958         case LFUN_WORDRIGHT:
959         case LFUN_WORDRIGHTSEL:
960         case LFUN_WORDSEL:
961         case LFUN_DOWN_PARAGRAPH:
962         case LFUN_DOWN_PARAGRAPHSEL:
963         case LFUN_UP_PARAGRAPH:
964         case LFUN_UP_PARAGRAPHSEL:
965         case LFUN_BACKSPACE:
966         case LFUN_HOME:
967         case LFUN_HOMESEL:
968         case LFUN_END:
969         case LFUN_ENDSEL:
970         case LFUN_BEGINNINGBUF:
971         case LFUN_BEGINNINGBUFSEL:
972         case LFUN_ENDBUF:
973         case LFUN_ENDBUFSEL:
974                 break;
975         case LFUN_LAYOUT_TABULAR: {
976                 InsetTabularMailer(*this).showDialog(bv);
977                 break;
978         }
979         case LFUN_INSET_DIALOG_UPDATE: {
980                 InsetTabularMailer(*this).updateDialog(bv);
981                 break;
982         }
983         case LFUN_TABULAR_FEATURE:
984                 if (!tabularFeatures(bv, arg))
985                         result = UNDISPATCHED;
986                 break;
987                 // insert file functions
988         case LFUN_FILE_INSERT_ASCII_PARA:
989         case LFUN_FILE_INSERT_ASCII:
990         {
991                 string tmpstr = getContentsOfAsciiFile(bv, arg, false);
992                 if (tmpstr.empty())
993                         break;
994                 if (insertAsciiString(bv, tmpstr, false))
995                         updateLocal(bv, INIT);
996                 else
997                         result = UNDISPATCHED;
998                 break;
999         }
1000         // cut and paste functions
1001         case LFUN_CUT:
1002                 if (!copySelection(bv))
1003                         break;
1004                 // no break here!
1005         case LFUN_DELETE:
1006                 recordUndo(bv, Undo::DELETE);
1007                 cutSelection(bv->buffer()->params);
1008                 updateLocal(bv, INIT);
1009                 break;
1010         case LFUN_COPY:
1011                 if (!hasSelection())
1012                         break;
1013                 finishUndo();
1014                 copySelection(bv);
1015                 break;
1016         case LFUN_PASTESELECTION:
1017         {
1018                 string const clip(bv->getClipboard());
1019                         if (clip.empty())
1020                         break;
1021 #if 0
1022                 if (clip.find('\t') != string::npos) {
1023                         int cols = 1;
1024                         int rows = 1;
1025                         int maxCols = 1;
1026                         string::size_type len = clip.length();
1027                         string::size_type p = 0;
1028
1029                         while (p < len &&
1030                               ((p = clip.find_first_of("\t\n", p)) != string::npos)) {
1031                                 switch (clip[p]) {
1032                                 case '\t':
1033                                         ++cols;
1034                                         break;
1035                                 case '\n':
1036                                         if ((p+1) < len)
1037                                                 ++rows;
1038                                         maxCols = max(cols, maxCols);
1039                                         cols = 1;
1040                                         break;
1041                                 }
1042                                 ++p;
1043                         }
1044                         maxCols = max(cols, maxCols);
1045                         delete paste_tabular;
1046                         paste_tabular = new LyXTabular(bv->buffer()->params,
1047                                                        this, rows, maxCols);
1048                         string::size_type op = 0;
1049                         int cell = 0;
1050                         int cells = paste_tabular->getNumberOfCells();
1051                         p = cols = 0;
1052                         while ((cell < cells) && (p < len) &&
1053                               (p = clip.find_first_of("\t\n", p)) != string::npos) {
1054                                 if (p >= len)
1055                                         break;
1056                                 switch (clip[p]) {
1057                                 case '\t':
1058                                         paste_tabular->getCellInset(cell)->setText(clip.substr(op, p-op));
1059                                         ++cols;
1060                                         ++cell;
1061                                         break;
1062                                 case '\n':
1063                                         paste_tabular->getCellInset(cell)->setText(clip.substr(op, p-op));
1064                                         while (cols++ < maxCols)
1065                                                 ++cell;
1066                                         cols = 0;
1067                                         break;
1068                                 }
1069                                 ++p;
1070                                 op = p;
1071                         }
1072                         // check for the last cell if there is no trailing '\n'
1073                         if ((cell < cells) && (op < len))
1074                                 paste_tabular->getCellInset(cell)->setText(clip.substr(op, len-op));
1075                 } else
1076 #else
1077                 if (!insertAsciiString(bv, clip, true))
1078 #endif
1079                 {
1080                         // so that the clipboard is used and it goes on
1081                         // to default
1082                         // and executes LFUN_PASTESELECTION in insettext!
1083                         delete paste_tabular;
1084                         paste_tabular = 0;
1085                 }
1086         }
1087         case LFUN_PASTE:
1088                 if (hasPasteBuffer()) {
1089                         recordUndo(bv, Undo::INSERT);
1090                         pasteSelection(bv);
1091                         updateLocal(bv, INIT);
1092                         break;
1093                 }
1094                 // ATTENTION: the function above has to be PASTE and PASTESELECTION!!!
1095         default:
1096                 // handle font changing stuff on selection before we lock the inset
1097                 // in the default part!
1098                 result = UNDISPATCHED;
1099                 if (hs) {
1100                         switch(action) {
1101                         case LFUN_LANGUAGE:
1102                         case LFUN_EMPH:
1103                         case LFUN_BOLD:
1104                         case LFUN_NOUN:
1105                         case LFUN_CODE:
1106                         case LFUN_SANS:
1107                         case LFUN_ROMAN:
1108                         case LFUN_DEFAULT:
1109                         case LFUN_UNDERLINE:
1110                         case LFUN_FONT_SIZE:
1111                                 if (bv->dispatch(FuncRequest(bv, action, arg)))
1112                                         result = DISPATCHED;
1113                                 break;
1114                         default:
1115                                 break;
1116                         }
1117                 }
1118                 // we try to activate the actual inset and put this event down to
1119                 // the insets dispatch function.
1120                 if (result == DISPATCHED || the_locking_inset)
1121                         break;
1122                 if (activateCellInset(bv)) {
1123                         // reset need_update setted in above function!
1124                         need_update = NONE;
1125                         result = the_locking_inset->localDispatch(FuncRequest(bv, action, arg));
1126                         if (result == UNDISPATCHED || result >= FINISHED) {
1127                                 unlockInsetInInset(bv, the_locking_inset);
1128                                 // we need to update if this was requested before
1129                                 updateLocal(bv, NONE);
1130                                 return UNDISPATCHED;
1131                         } else if (hs) {
1132                                 clearSelection();
1133                                 // so the below CELL is not set because this is higher
1134                                 // priority and we get a full redraw
1135                                 need_update = FULL;
1136                         }
1137                         updateLocal(bv, CELL);
1138                         return result;
1139                 }
1140                 break;
1141         }
1142         if (result < FINISHED) {
1143                 if (!the_locking_inset && bv->fitCursor())
1144                         updateLocal(bv, FULL);
1145         } else
1146                 bv->unlockInset(this);
1147         return result;
1148 }
1149
1150
1151 int InsetTabular::latex(Buffer const * buf, ostream & os,
1152                         LatexRunParams const & runparams) const
1153 {
1154         return tabular.latex(buf, os, runparams);
1155 }
1156
1157
1158 int InsetTabular::ascii(Buffer const * buf, ostream & os, int ll) const
1159 {
1160         if (ll > 0)
1161                 return tabular.ascii(buf, os, (int)parOwner()->params().depth(),
1162                                       false, 0);
1163         return tabular.ascii(buf, os, 0, false, 0);
1164 }
1165
1166
1167 int InsetTabular::linuxdoc(Buffer const * buf, ostream & os) const
1168 {
1169         return tabular.linuxdoc(buf,os);
1170 }
1171
1172
1173 int InsetTabular::docbook(Buffer const * buf, ostream & os, bool mixcont) const
1174 {
1175         int ret = 0;
1176         InsetOld * master;
1177
1178         // if the table is inside a float it doesn't need the informaltable
1179         // wrapper. Search for it.
1180         for (master = owner();
1181              master && master->lyxCode() != InsetOld::FLOAT_CODE;
1182              master = master->owner());
1183
1184         if (!master) {
1185                 os << "<informaltable>";
1186                 if (mixcont)
1187                         os << endl;
1188                 ++ret;
1189         }
1190         ret += tabular.docbook(buf, os, mixcont);
1191         if (!master) {
1192                 os << "</informaltable>";
1193                 if (mixcont)
1194                         os << endl;
1195                 ++ret;
1196         }
1197         return ret;
1198 }
1199
1200
1201 void InsetTabular::validate(LaTeXFeatures & features) const
1202 {
1203         tabular.validate(features);
1204 }
1205
1206
1207 void InsetTabular::calculate_dimensions_of_cells(MetricsInfo & mi) const
1208 {
1209 #if 1
1210         // if we have a locking_inset we should have to check only this cell for
1211         // change so I'll try this to have a boost, but who knows ;) (Jug?)
1212         // This is _really_ important (André)
1213         if (need_update != INIT &&
1214             the_locking_inset == &tabular.getCellInset(actcell)) {
1215                 int maxAsc = 0;
1216                 int maxDesc = 0;
1217                 for (int j = 0; j < tabular.columns(); ++j) {
1218                         Dimension dim;
1219                         MetricsInfo m = mi;
1220                         m.base.textwidth =
1221                                 tabular.column_info[j].p_width.inPixels(mi.base.textwidth);
1222                         tabular.getCellInset(actrow, j).metrics(m, dim);
1223                         maxAsc  = max(dim.asc, maxAsc);
1224                         maxDesc = max(dim.des, maxDesc);
1225                 }
1226                 tabular.setWidthOfCell(actcell, the_locking_inset->width());
1227                 tabular.setAscentOfRow(actrow, maxAsc + ADD_TO_HEIGHT);
1228                 tabular.setDescentOfRow(actrow, maxDesc + ADD_TO_HEIGHT);
1229                 return;
1230         }
1231 #endif
1232
1233         int cell = -1;
1234         bool changed = false;
1235         for (int i = 0; i < tabular.rows(); ++i) {
1236                 int maxAsc = 0;
1237                 int maxDesc = 0;
1238                 for (int j = 0; j < tabular.columns(); ++j) {
1239                         if (tabular.isPartOfMultiColumn(i, j))
1240                                 continue;
1241                         ++cell;
1242                         Dimension dim;
1243                         MetricsInfo m = mi;
1244                         m.base.textwidth =
1245                                 tabular.column_info[j].p_width.inPixels(mi.base.textwidth);
1246                         tabular.getCellInset(cell).metrics(m, dim);
1247                         maxAsc  = max(maxAsc, dim.asc);
1248                         maxDesc = max(maxDesc, dim.des);
1249                         tabular.setWidthOfCell(cell, dim.wid);
1250                 }
1251                 tabular.setAscentOfRow(i, maxAsc + ADD_TO_HEIGHT);
1252                 tabular.setDescentOfRow(i, maxDesc + ADD_TO_HEIGHT);
1253         }
1254 }
1255
1256
1257 void InsetTabular::getCursor(BufferView & bv, int & x, int & y) const
1258 {
1259         if (the_locking_inset) {
1260                 the_locking_inset->getCursor(bv, x, y);
1261                 return;
1262         }
1263
1264         x = cursorx_;
1265         y = cursory_ + InsetTabular::y();
1266
1267         // Fun stuff
1268         int desc = tabular.getDescentOfRow(actrow);
1269         y += desc;
1270         int ascdesc = tabular.getAscentOfRow(actrow) + desc;
1271         y -= ascdesc / 2;
1272         y += ADD_TO_HEIGHT * 2;
1273         y += TEXT_TO_INSET_OFFSET;
1274 }
1275
1276
1277 void InsetTabular::getCursorPos(BufferView * bv, int & x, int & y) const
1278 {
1279         if (the_locking_inset) {
1280                 the_locking_inset->getCursorPos(bv, x, y);
1281                 return;
1282         }
1283         x = cursorx_ - top_x;
1284         y = cursory_;
1285 }
1286
1287
1288 void InsetTabular::fitInsetCursor(BufferView * bv) const
1289 {
1290         if (the_locking_inset) {
1291                 int old_top_y = bv->text->top_y();
1292                 the_locking_inset->fitInsetCursor(bv);
1293                 if (old_top_y != bv->text->top_y())
1294                         need_update = FULL;
1295                 return;
1296         }
1297         LyXFont font;
1298
1299         int const asc = font_metrics::maxAscent(font);
1300         int const desc = font_metrics::maxDescent(font);
1301         resetPos(bv);
1302
1303         if (bv->fitLockedInsetCursor(cursorx_, cursory_, asc, desc))
1304                 need_update = FULL;
1305 }
1306
1307
1308 void InsetTabular::setPos(BufferView * bv, int x, int y) const
1309 {
1310         cursory_ = 0;
1311         actcell = actrow = actcol = 0;
1312         int ly = tabular.getDescentOfRow(actrow);
1313
1314         // first search the right row
1315         while (ly < y && actrow + 1 < tabular.rows()) {
1316                 cursory_ += tabular.getDescentOfRow(actrow) +
1317                                  tabular.getAscentOfRow(actrow + 1) +
1318                                  tabular.getAdditionalHeight(actrow + 1);
1319                 ++actrow;
1320                 ly = cursory_ + tabular.getDescentOfRow(actrow);
1321         }
1322         actcell = tabular.getCellNumber(actrow, actcol);
1323
1324         // now search the right column
1325         int lx = tabular.getWidthOfColumn(actcell) -
1326                 tabular.getAdditionalWidth(actcell);
1327         for (; !tabular.isLastCellInRow(actcell) && lx < x; ++actcell) {
1328                 lx += tabular.getWidthOfColumn(actcell + 1)
1329                         + tabular.getAdditionalWidth(actcell);
1330         }
1331         cursorx_ = lx - tabular.getWidthOfColumn(actcell) + top_x + 2;
1332         resetPos(bv);
1333 }
1334
1335
1336 int InsetTabular::getCellXPos(int cell) const
1337 {
1338         int c = cell;
1339
1340         for (; !tabular.isFirstCellInRow(c); --c)
1341                 ;
1342         int lx = tabular.getWidthOfColumn(cell);
1343         for (; c < cell; ++c)
1344                 lx += tabular.getWidthOfColumn(c);
1345
1346         return (lx - tabular.getWidthOfColumn(cell) + top_x);
1347 }
1348
1349
1350 void InsetTabular::resetPos(BufferView * bv) const
1351 {
1352 #ifdef WITH_WARNINGS
1353 #warning This should be fixed in the right manner (20011128 Jug)
1354 #endif
1355         // fast hack to fix infinite repaintings!
1356         if (in_reset_pos > 0)
1357                 return;
1358
1359         int cell = 0;
1360         actcol = tabular.column_of_cell(actcell);
1361         actrow = 0;
1362         cursory_ = 0;
1363         for (; cell < actcell && !tabular.isLastRow(cell); ++cell) {
1364                 if (tabular.isLastCellInRow(cell)) {
1365                         cursory_ += tabular.getDescentOfRow(actrow) +
1366                                          tabular.getAscentOfRow(actrow + 1) +
1367                                          tabular.getAdditionalHeight(actrow + 1);
1368                         ++actrow;
1369                 }
1370         }
1371         if (!locked) {
1372                 if (the_locking_inset)
1373                         inset_y = cursory_;
1374                 return;
1375         }
1376         // we need this only from here on!!!
1377         ++in_reset_pos;
1378         static int const offset = ADD_TO_TABULAR_WIDTH + 2;
1379         int new_x = getCellXPos(actcell);
1380         int old_x = cursorx_;
1381         new_x += offset;
1382         cursorx_ = new_x;
1383 //    cursor.x(getCellXPos(actcell) + offset);
1384         if (actcol < tabular.columns() - 1 && scroll(false) &&
1385                 tabular.getWidthOfTabular() < bv->workWidth()-20)
1386         {
1387                 scroll(bv, 0.0F);
1388                 updateLocal(bv, FULL);
1389         } else if (the_locking_inset &&
1390                  tabular.getWidthOfColumn(actcell) > bv->workWidth() - 20)
1391         {
1392                 int xx = cursorx_ - offset + bv->text->getRealCursorX();
1393                 if (xx > bv->workWidth()-20) {
1394                         scroll(bv, -(xx - bv->workWidth() + 60));
1395                         updateLocal(bv, FULL);
1396                 } else if (xx < 20) {
1397                         if (xx < 0)
1398                                 xx = -xx + 60;
1399                         else
1400                                 xx = 60;
1401                         scroll(bv, xx);
1402                         updateLocal(bv, FULL);
1403                 }
1404         } else if (cursorx_ - offset > 20 &&
1405                    cursorx_ - offset + tabular.getWidthOfColumn(actcell)
1406                    > bv->workWidth() - 20) {
1407                 scroll(bv, -tabular.getWidthOfColumn(actcell) - 20);
1408                 updateLocal(bv, FULL);
1409         } else if (cursorx_ - offset < 20) {
1410                 scroll(bv, 20 - cursorx_ + offset);
1411                 updateLocal(bv, FULL);
1412         } else if (scroll() && top_x > 20 &&
1413                    (top_x + tabular.getWidthOfTabular()) > bv->workWidth() - 20) {
1414                 scroll(bv, old_x - cursorx_);
1415                 updateLocal(bv, FULL);
1416         }
1417         if (the_locking_inset) {
1418                 inset_x = cursorx_ - top_x + tabular.getBeginningOfTextInCell(actcell);
1419                 inset_y = cursory_;
1420         }
1421         if ((!the_locking_inset ||
1422              !the_locking_inset->getFirstLockingInsetOfType(TABULAR_CODE)) &&
1423             actcell != oldcell) {
1424                 InsetTabularMailer(*this).updateDialog(bv);
1425                 oldcell = actcell;
1426         }
1427         in_reset_pos = 0;
1428 }
1429
1430
1431 InsetOld::RESULT InsetTabular::moveRight(BufferView * bv, bool lock)
1432 {
1433         if (lock && !old_locking_inset) {
1434                 if (activateCellInset(bv))
1435                         return DISPATCHED;
1436         } else {
1437                 bool moved = isRightToLeft(bv)
1438                         ? movePrevCell(bv) : moveNextCell(bv);
1439                 if (!moved)
1440                         return FINISHED_RIGHT;
1441                 if (lock && activateCellInset(bv))
1442                         return DISPATCHED;
1443         }
1444         resetPos(bv);
1445         return DISPATCHED_NOUPDATE;
1446 }
1447
1448
1449 InsetOld::RESULT InsetTabular::moveLeft(BufferView * bv, bool lock)
1450 {
1451         bool moved = isRightToLeft(bv) ? moveNextCell(bv) : movePrevCell(bv);
1452         if (!moved)
1453                 return FINISHED;
1454         if (lock) {       // behind the inset
1455                 if (activateCellInset(bv, 0, 0, mouse_button::none, true))
1456                         return DISPATCHED;
1457         }
1458         resetPos(bv);
1459         return DISPATCHED_NOUPDATE;
1460 }
1461
1462
1463 InsetOld::RESULT InsetTabular::moveUp(BufferView * bv, bool lock)
1464 {
1465         int const ocell = actcell;
1466         actcell = tabular.getCellAbove(actcell);
1467         if (actcell == ocell) // we moved out of the inset
1468                 return FINISHED_UP;
1469         resetPos(bv);
1470         if (lock) {
1471                 int x = 0;
1472                 int y = 0;
1473                 if (old_locking_inset) {
1474                         old_locking_inset->getCursorPos(bv, x, y);
1475                         x -= cursorx_ + tabular.getBeginningOfTextInCell(actcell);
1476                 }
1477                 if (activateCellInset(bv, x, 0))
1478                         return DISPATCHED;
1479         }
1480         return DISPATCHED_NOUPDATE;
1481 }
1482
1483
1484 InsetOld::RESULT InsetTabular::moveDown(BufferView * bv, bool lock)
1485 {
1486         int const ocell = actcell;
1487         actcell = tabular.getCellBelow(actcell);
1488         if (actcell == ocell) // we moved out of the inset
1489                 return FINISHED_DOWN;
1490         resetPos(bv);
1491         if (lock) {
1492                 int x = 0;
1493                 int y = 0;
1494                 if (old_locking_inset) {
1495                         old_locking_inset->getCursorPos(bv, x, y);
1496                         x -= cursorx_ + tabular.getBeginningOfTextInCell(actcell);
1497                 }
1498                 if (activateCellInset(bv, x, 0))
1499                         return DISPATCHED;
1500         }
1501         return DISPATCHED_NOUPDATE;
1502 }
1503
1504
1505 bool InsetTabular::moveNextCell(BufferView * bv, bool lock)
1506 {
1507         if (isRightToLeft(bv)) {
1508                 if (tabular.isFirstCellInRow(actcell)) {
1509                         int row = tabular.row_of_cell(actcell);
1510                         if (row == tabular.rows() - 1)
1511                                 return false;
1512                         actcell = tabular.getLastCellInRow(row);
1513                         actcell = tabular.getCellBelow(actcell);
1514                 } else {
1515                         if (!actcell)
1516                                 return false;
1517                         --actcell;
1518                 }
1519         } else {
1520                 if (tabular.isLastCell(actcell))
1521                         return false;
1522                 ++actcell;
1523         }
1524         if (lock) {
1525                 bool rtl = tabular.getCellInset(actcell).paragraphs.begin()->
1526                         isRightToLeftPar(bv->buffer()->params);
1527                 activateCellInset(bv, 0, 0, mouse_button::none, !rtl);
1528         }
1529         resetPos(bv);
1530         return true;
1531 }
1532
1533
1534 bool InsetTabular::movePrevCell(BufferView * bv, bool lock)
1535 {
1536         if (isRightToLeft(bv)) {
1537                 if (tabular.isLastCellInRow(actcell)) {
1538                         int row = tabular.row_of_cell(actcell);
1539                         if (row == 0)
1540                                 return false;
1541                         actcell = tabular.getFirstCellInRow(row);
1542                         actcell = tabular.getCellAbove(actcell);
1543                 } else {
1544                         if (tabular.isLastCell(actcell))
1545                                 return false;
1546                         ++actcell;
1547                 }
1548         } else {
1549                 if (!actcell) // first cell
1550                         return false;
1551                 --actcell;
1552         }
1553         if (lock) {
1554                 bool rtl = tabular.getCellInset(actcell).paragraphs.begin()->
1555                         isRightToLeftPar(bv->buffer()->params);
1556                 activateCellInset(bv, 0, 0, mouse_button::none, !rtl);
1557         }
1558         resetPos(bv);
1559         return true;
1560 }
1561
1562
1563 void InsetTabular::setFont(BufferView * bv, LyXFont const & font, bool tall,
1564                            bool selectall)
1565 {
1566         if (selectall) {
1567                 setSelection(0, tabular.getNumberOfCells() - 1);
1568         }
1569         if (hasSelection()) {
1570                 recordUndo(bv, Undo::ATOMIC);
1571                 bool const frozen = undo_frozen;
1572                 if (!frozen)
1573                         freezeUndo();
1574                 // apply the fontchange on the whole selection
1575                 int sel_row_start;
1576                 int sel_row_end;
1577                 int sel_col_start;
1578                 int sel_col_end;
1579                 getSelection(sel_row_start, sel_row_end, sel_col_start, sel_col_end);
1580                 for(int i = sel_row_start; i <= sel_row_end; ++i)
1581                         for(int j = sel_col_start; j <= sel_col_end; ++j)
1582                                 tabular.getCellInset(i, j).setFont(bv, font, tall, true);
1583
1584                 if (!frozen)
1585                         unFreezeUndo();
1586                 if (selectall)
1587                         clearSelection();
1588                 updateLocal(bv, INIT);
1589         }
1590         if (the_locking_inset)
1591                 the_locking_inset->setFont(bv, font, tall);
1592 }
1593
1594
1595 bool InsetTabular::tabularFeatures(BufferView * bv, string const & what)
1596 {
1597         LyXTabular::Feature action = LyXTabular::LAST_ACTION;
1598
1599         int i = 0;
1600         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
1601                 string const tmp = tabularFeature[i].feature;
1602
1603                 if (tmp == what.substr(0, tmp.length())) {
1604                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
1605                         //tabularFeatures[i].feature.length())) {
1606                         action = tabularFeature[i].action;
1607                         break;
1608                 }
1609         }
1610         if (action == LyXTabular::LAST_ACTION)
1611                 return false;
1612
1613         string const val =
1614                 ltrim(what.substr(tabularFeature[i].feature.length()));
1615         tabularFeatures(bv, action, val);
1616         return true;
1617 }
1618
1619 namespace {
1620
1621 void checkLongtableSpecial(LyXTabular::ltType & ltt,
1622                           string const & special, bool & flag)
1623 {
1624         if (special == "dl_above") {
1625                 ltt.topDL = flag;
1626                 ltt.set = false;
1627         } else if (special == "dl_below") {
1628                 ltt.bottomDL = flag;
1629                 ltt.set = false;
1630         } else if (special == "empty") {
1631                 ltt.empty = flag;
1632                 ltt.set = false;
1633         } else if (flag) {
1634                 ltt.empty = false;
1635                 ltt.set = true;
1636         }
1637 }
1638
1639 }
1640
1641
1642 void InsetTabular::tabularFeatures(BufferView * bv,
1643                                    LyXTabular::Feature feature,
1644                                    string const & value)
1645 {
1646         int sel_col_start;
1647         int sel_col_end;
1648         int sel_row_start;
1649         int sel_row_end;
1650         bool setLines = false;
1651         LyXAlignment setAlign = LYX_ALIGN_LEFT;
1652         LyXTabular::VAlignment setVAlign = LyXTabular::LYX_VALIGN_TOP;
1653
1654         switch (feature) {
1655
1656         case LyXTabular::M_ALIGN_LEFT:
1657         case LyXTabular::ALIGN_LEFT:
1658                 setAlign = LYX_ALIGN_LEFT;
1659                 break;
1660
1661         case LyXTabular::M_ALIGN_RIGHT:
1662         case LyXTabular::ALIGN_RIGHT:
1663                 setAlign = LYX_ALIGN_RIGHT;
1664                 break;
1665
1666         case LyXTabular::M_ALIGN_CENTER:
1667         case LyXTabular::ALIGN_CENTER:
1668                 setAlign = LYX_ALIGN_CENTER;
1669                 break;
1670
1671         case LyXTabular::ALIGN_BLOCK:
1672                 setAlign = LYX_ALIGN_BLOCK;
1673                 break;
1674
1675         case LyXTabular::M_VALIGN_TOP:
1676         case LyXTabular::VALIGN_TOP:
1677                 setVAlign = LyXTabular::LYX_VALIGN_TOP;
1678                 break;
1679
1680         case LyXTabular::M_VALIGN_BOTTOM:
1681         case LyXTabular::VALIGN_BOTTOM:
1682                 setVAlign = LyXTabular::LYX_VALIGN_BOTTOM;
1683                 break;
1684
1685         case LyXTabular::M_VALIGN_CENTER:
1686         case LyXTabular::VALIGN_CENTER:
1687                 setVAlign = LyXTabular::LYX_VALIGN_CENTER;
1688                 break;
1689
1690         default:
1691                 break;
1692         }
1693
1694         if (hasSelection()) {
1695                 getSelection(sel_row_start, sel_row_end, sel_col_start, sel_col_end);
1696         } else {
1697                 sel_col_start = sel_col_end = tabular.column_of_cell(actcell);
1698                 sel_row_start = sel_row_end = tabular.row_of_cell(actcell);
1699         }
1700         recordUndo(bv, Undo::ATOMIC);
1701
1702         int row =  tabular.row_of_cell(actcell);
1703         int column = tabular.column_of_cell(actcell);
1704         bool flag = true;
1705         LyXTabular::ltType ltt;
1706
1707         switch (feature) {
1708
1709         case LyXTabular::SET_PWIDTH:
1710         {
1711                 LyXLength const vallen(value);
1712                 LyXLength const & tmplen = tabular.getColumnPWidth(actcell);
1713
1714                 bool const update = (tmplen != vallen);
1715                 tabular.setColumnPWidth(actcell, vallen);
1716                 if (update) {
1717                         // We need this otherwise we won't resize
1718                         // the insettext of the active cell (if any)
1719                         // until later (see InsetText::do_resize)
1720                         unlockInsetInInset(bv, the_locking_inset);
1721
1722                         for (int i = 0; i < tabular.rows(); ++i)
1723                                 tabular.getCellInset(i, column).resizeLyXText(bv);
1724                         updateLocal(bv, INIT);
1725                 }
1726
1727                 if (vallen.zero()
1728                     && tabular.getAlignment(actcell, true) == LYX_ALIGN_BLOCK)
1729                         tabularFeatures(bv, LyXTabular::ALIGN_CENTER, string());
1730                 else if (!vallen.zero()
1731                          && tabular.getAlignment(actcell, true) != LYX_ALIGN_BLOCK)
1732                         tabularFeatures(bv, LyXTabular::ALIGN_BLOCK, string());
1733                 break;
1734         }
1735
1736         case LyXTabular::SET_MPWIDTH:
1737         {
1738                 LyXLength const vallen(value);
1739                 LyXLength const & tmplen = tabular.getPWidth(actcell);
1740
1741                 bool const update = (tmplen != vallen);
1742                 tabular.setMColumnPWidth(actcell, vallen);
1743                 if (update) {
1744                         // We need this otherwise we won't resize
1745                         // the insettext of the active cell (if any)
1746                         // until later (see InsetText::do_resize)
1747                         unlockInsetInInset(bv, the_locking_inset);
1748
1749                         for (int i = 0; i < tabular.rows(); ++i)
1750                                 tabular.getCellInset(i, column).resizeLyXText(bv);
1751
1752                         updateLocal(bv, INIT);
1753                 }
1754         }
1755         break;
1756         case LyXTabular::SET_SPECIAL_COLUMN:
1757         case LyXTabular::SET_SPECIAL_MULTI:
1758                 tabular.setAlignSpecial(actcell,value,feature);
1759                 updateLocal(bv, FULL);
1760                 break;
1761         case LyXTabular::APPEND_ROW:
1762                 // append the row into the tabular
1763                 unlockInsetInInset(bv, the_locking_inset);
1764                 tabular.appendRow(bv->buffer()->params, actcell);
1765                 updateLocal(bv, INIT);
1766                 break;
1767         case LyXTabular::APPEND_COLUMN:
1768                 // append the column into the tabular
1769                 unlockInsetInInset(bv, the_locking_inset);
1770                 tabular.appendColumn(bv->buffer()->params, actcell);
1771                 actcell = tabular.getCellNumber(row, column);
1772                 updateLocal(bv, INIT);
1773                 break;
1774         case LyXTabular::DELETE_ROW:
1775                 unlockInsetInInset(bv, the_locking_inset);
1776                 for(int i = sel_row_start; i <= sel_row_end; ++i) {
1777                         tabular.deleteRow(sel_row_start);
1778                 }
1779                 if (sel_row_start >= tabular.rows())
1780                         --sel_row_start;
1781                 actcell = tabular.getCellNumber(sel_row_start, column);
1782                 clearSelection();
1783                 updateLocal(bv, INIT);
1784                 break;
1785         case LyXTabular::DELETE_COLUMN:
1786                 unlockInsetInInset(bv, the_locking_inset);
1787                 for(int i = sel_col_start; i <= sel_col_end; ++i) {
1788                         tabular.deleteColumn(sel_col_start);
1789                 }
1790                 if (sel_col_start >= tabular.columns())
1791                         --sel_col_start;
1792                 actcell = tabular.getCellNumber(row, sel_col_start);
1793                 clearSelection();
1794                 updateLocal(bv, INIT);
1795                 break;
1796         case LyXTabular::M_TOGGLE_LINE_TOP:
1797                 flag = false;
1798         case LyXTabular::TOGGLE_LINE_TOP:
1799         {
1800                 bool lineSet = !tabular.topLine(actcell, flag);
1801                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1802                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1803                                 tabular.setTopLine(
1804                                         tabular.getCellNumber(i, j),
1805                                         lineSet, flag);
1806                 updateLocal(bv, INIT);
1807                 break;
1808         }
1809
1810         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
1811                 flag = false;
1812         case LyXTabular::TOGGLE_LINE_BOTTOM:
1813         {
1814                 bool lineSet = !tabular.bottomLine(actcell, flag);
1815                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1816                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1817                                 tabular.setBottomLine(
1818                                         tabular.getCellNumber(i, j),
1819                                         lineSet,
1820                                         flag);
1821                 updateLocal(bv, INIT);
1822                 break;
1823         }
1824
1825         case LyXTabular::M_TOGGLE_LINE_LEFT:
1826                 flag = false;
1827         case LyXTabular::TOGGLE_LINE_LEFT:
1828         {
1829                 bool lineSet = !tabular.leftLine(actcell, flag);
1830                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1831                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1832                                 tabular.setLeftLine(
1833                                         tabular.getCellNumber(i,j),
1834                                         lineSet,
1835                                         flag);
1836                 updateLocal(bv, INIT);
1837                 break;
1838         }
1839
1840         case LyXTabular::M_TOGGLE_LINE_RIGHT:
1841                 flag = false;
1842         case LyXTabular::TOGGLE_LINE_RIGHT:
1843         {
1844                 bool lineSet = !tabular.rightLine(actcell, flag);
1845                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1846                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1847                                 tabular.setRightLine(
1848                                         tabular.getCellNumber(i,j),
1849                                         lineSet,
1850                                         flag);
1851                 updateLocal(bv, INIT);
1852                 break;
1853         }
1854
1855         case LyXTabular::M_ALIGN_LEFT:
1856         case LyXTabular::M_ALIGN_RIGHT:
1857         case LyXTabular::M_ALIGN_CENTER:
1858                 flag = false;
1859         case LyXTabular::ALIGN_LEFT:
1860         case LyXTabular::ALIGN_RIGHT:
1861         case LyXTabular::ALIGN_CENTER:
1862         case LyXTabular::ALIGN_BLOCK:
1863                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1864                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1865                                 tabular.setAlignment(
1866                                         tabular.getCellNumber(i, j),
1867                                         setAlign,
1868                                         flag);
1869                 updateLocal(bv, INIT);
1870                 break;
1871
1872         case LyXTabular::M_VALIGN_TOP:
1873         case LyXTabular::M_VALIGN_BOTTOM:
1874         case LyXTabular::M_VALIGN_CENTER:
1875                 flag = false;
1876         case LyXTabular::VALIGN_TOP:
1877         case LyXTabular::VALIGN_BOTTOM:
1878         case LyXTabular::VALIGN_CENTER:
1879                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1880                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1881                                 tabular.setVAlignment(
1882                                         tabular.getCellNumber(i, j),
1883                                         setVAlign, flag);
1884                 updateLocal(bv, INIT);
1885                 break;
1886
1887         case LyXTabular::MULTICOLUMN: {
1888                 if (sel_row_start != sel_row_end) {
1889 #ifdef WITH_WARNINGS
1890 #warning Need I say it ? This is horrible.
1891 #endif
1892                         Alert::error(_("Error setting multicolumn"),
1893                                    _("You cannot set multicolumn vertically."));
1894                         return;
1895                 }
1896                 // just multicol for one Single Cell
1897                 if (!hasSelection()) {
1898                         // check wether we are completly in a multicol
1899                         if (tabular.isMultiColumn(actcell)) {
1900                                 tabular.unsetMultiColumn(actcell);
1901                                 updateLocal(bv, INIT);
1902                         } else {
1903                                 tabular.setMultiColumn(bv->buffer(), actcell, 1);
1904                                 updateLocal(bv, CELL);
1905                         }
1906                         break;
1907                 }
1908                 // we have a selection so this means we just add all this
1909                 // cells to form a multicolumn cell
1910                 int s_start;
1911                 int s_end;
1912
1913                 if (sel_cell_start > sel_cell_end) {
1914                         s_start = sel_cell_end;
1915                         s_end = sel_cell_start;
1916                 } else {
1917                         s_start = sel_cell_start;
1918                         s_end = sel_cell_end;
1919                 }
1920                 tabular.setMultiColumn(bv->buffer(), s_start, s_end - s_start + 1);
1921                 actcell = s_start;
1922                 clearSelection();
1923                 updateLocal(bv, INIT);
1924                 break;
1925         }
1926
1927         case LyXTabular::SET_ALL_LINES:
1928                 setLines = true;
1929         case LyXTabular::UNSET_ALL_LINES:
1930                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1931                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1932                                 tabular.setAllLines(
1933                                         tabular.getCellNumber(i,j), setLines);
1934                 updateLocal(bv, INIT);
1935                 break;
1936
1937         case LyXTabular::SET_LONGTABULAR:
1938                 tabular.setLongTabular(true);
1939                 updateLocal(bv, INIT); // because this toggles displayed
1940                 break;
1941
1942         case LyXTabular::UNSET_LONGTABULAR:
1943                 tabular.setLongTabular(false);
1944                 updateLocal(bv, INIT); // because this toggles displayed
1945                 break;
1946
1947         case LyXTabular::SET_ROTATE_TABULAR:
1948                 tabular.setRotateTabular(true);
1949                 break;
1950
1951         case LyXTabular::UNSET_ROTATE_TABULAR:
1952                 tabular.setRotateTabular(false);
1953                 break;
1954
1955         case LyXTabular::SET_ROTATE_CELL:
1956                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1957                         for (int j = sel_col_start; j<=sel_col_end; ++j)
1958                                 tabular.setRotateCell(
1959                                         tabular.getCellNumber(i, j),
1960                                         true);
1961                 break;
1962
1963         case LyXTabular::UNSET_ROTATE_CELL:
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.setRotateCell(
1967                                         tabular.getCellNumber(i, j), false);
1968                 break;
1969
1970         case LyXTabular::SET_USEBOX: {
1971                 LyXTabular::BoxType val = LyXTabular::BoxType(strToInt(value));
1972                 if (val == tabular.getUsebox(actcell))
1973                         val = LyXTabular::BOX_NONE;
1974                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1975                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1976                                 tabular.setUsebox(tabular.getCellNumber(i, j), val);
1977                 break;
1978         }
1979
1980         case LyXTabular::UNSET_LTFIRSTHEAD:
1981                 flag = false;
1982         case LyXTabular::SET_LTFIRSTHEAD:
1983                 tabular.getRowOfLTFirstHead(row, ltt);
1984                 checkLongtableSpecial(ltt, value, flag);
1985                 tabular.setLTHead(row, flag, ltt, true);
1986                 break;
1987
1988         case LyXTabular::UNSET_LTHEAD:
1989                 flag = false;
1990         case LyXTabular::SET_LTHEAD:
1991                 tabular.getRowOfLTHead(row, ltt);
1992                 checkLongtableSpecial(ltt, value, flag);
1993                 tabular.setLTHead(row, flag, ltt, false);
1994                 break;
1995
1996         case LyXTabular::UNSET_LTFOOT:
1997                 flag = false;
1998         case LyXTabular::SET_LTFOOT:
1999                 tabular.getRowOfLTFoot(row, ltt);
2000                 checkLongtableSpecial(ltt, value, flag);
2001                 tabular.setLTFoot(row, flag, ltt, false);
2002                 break;
2003
2004         case LyXTabular::UNSET_LTLASTFOOT:
2005                 flag = false;
2006         case LyXTabular::SET_LTLASTFOOT:
2007                 tabular.getRowOfLTLastFoot(row, ltt);
2008                 checkLongtableSpecial(ltt, value, flag);
2009                 tabular.setLTFoot(row, flag, ltt, true);
2010                 break;
2011
2012         case LyXTabular::SET_LTNEWPAGE: {
2013                 bool what = !tabular.getLTNewPage(row);
2014                 tabular.setLTNewPage(row, what);
2015                 break;
2016         }
2017
2018         // dummy stuff just to avoid warnings
2019         case LyXTabular::LAST_ACTION:
2020                 break;
2021         }
2022
2023         InsetTabularMailer(*this).updateDialog(bv);
2024 }
2025
2026
2027 bool InsetTabular::activateCellInset(BufferView * bv, int x, int y,
2028         mouse_button::state button, bool behind)
2029 {
2030         UpdatableInset & inset = tabular.getCellInset(actcell);
2031         if (behind) {
2032 #warning metrics?
2033                 x = inset.x() + inset.width();
2034                 y = inset.descent();
2035         }
2036         //inset_x = cursorx_ - top_x + tabular.getBeginningOfTextInCell(actcell);
2037         //inset_y = cursory_;
2038         inset.localDispatch(FuncRequest(bv, LFUN_INSET_EDIT, x,  y, button));
2039         if (!the_locking_inset)
2040                 return false;
2041         updateLocal(bv, CELL);
2042         return (the_locking_inset != 0);
2043 }
2044
2045
2046 bool InsetTabular::activateCellInsetAbs(BufferView * bv, int x, int y,
2047                                         mouse_button::state button)
2048 {
2049         inset_x = cursorx_ - top_x + tabular.getBeginningOfTextInCell(actcell);
2050         inset_y = cursory_;
2051         return activateCellInset(bv, x - inset_x, y - inset_y, button);
2052 }
2053
2054
2055 bool InsetTabular::insetHit(BufferView *, int x, int) const
2056 {
2057         return x + top_x > cursorx_ + tabular.getBeginningOfTextInCell(actcell);
2058 }
2059
2060
2061 void InsetTabular::deleteLyXText(BufferView * bv, bool recursive) const
2062 {
2063         resizeLyXText(bv, recursive);
2064 }
2065
2066
2067 void InsetTabular::resizeLyXText(BufferView * bv, bool force) const
2068 {
2069         if (force)
2070                 for (int i = 0; i < tabular.rows(); ++i)
2071                         for (int j = 0; j < tabular.columns(); ++j)
2072                                 tabular.getCellInset(i, j).resizeLyXText(bv, true);
2073         need_update = FULL;
2074 }
2075
2076
2077 LyXText * InsetTabular::getLyXText(BufferView const * bv,
2078                                    bool const recursive) const
2079 {
2080         if (the_locking_inset)
2081                 return the_locking_inset->getLyXText(bv, recursive);
2082         return InsetOld::getLyXText(bv, recursive);
2083 }
2084
2085
2086 bool InsetTabular::showInsetDialog(BufferView * bv) const
2087 {
2088         if (!the_locking_inset || !the_locking_inset->showInsetDialog(bv))
2089                 InsetTabularMailer(*this).showDialog(bv);
2090         return true;
2091 }
2092
2093
2094 void InsetTabular::openLayoutDialog(BufferView * bv) const
2095 {
2096         if (the_locking_inset) {
2097                 InsetTabular * inset = static_cast<InsetTabular *>
2098                         (the_locking_inset->getFirstLockingInsetOfType(TABULAR_CODE));
2099                 if (inset) {
2100                         inset->openLayoutDialog(bv);
2101                         return;
2102                 }
2103         }
2104         InsetTabularMailer(*this).showDialog(bv);
2105 }
2106
2107
2108 //
2109 // function returns an object as defined in func_status.h:
2110 // states OK, Unknown, Disabled, On, Off.
2111 //
2112 FuncStatus InsetTabular::getStatus(string const & what) const
2113 {
2114         int action = LyXTabular::LAST_ACTION;
2115         FuncStatus status;
2116
2117         int i = 0;
2118         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
2119                 string const tmp = tabularFeature[i].feature;
2120                 if (tmp == what.substr(0, tmp.length())) {
2121                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
2122                         //   tabularFeatures[i].feature.length())) {
2123                         action = tabularFeature[i].action;
2124                         break;
2125                 }
2126         }
2127         if (action == LyXTabular::LAST_ACTION) {
2128                 status.clear();
2129                 return status.unknown(true);
2130         }
2131
2132         string const argument
2133                 = ltrim(what.substr(tabularFeature[i].feature.length()));
2134
2135         int sel_row_start;
2136         int sel_row_end;
2137         int dummy;
2138         LyXTabular::ltType dummyltt;
2139         bool flag = true;
2140
2141         if (hasSelection())
2142                 getSelection(sel_row_start, sel_row_end, dummy, dummy);
2143         else
2144                 sel_row_start = sel_row_end = tabular.row_of_cell(actcell);
2145
2146         switch (action) {
2147         case LyXTabular::SET_PWIDTH:
2148         case LyXTabular::SET_MPWIDTH:
2149         case LyXTabular::SET_SPECIAL_COLUMN:
2150         case LyXTabular::SET_SPECIAL_MULTI:
2151         case LyXTabular::APPEND_ROW:
2152         case LyXTabular::APPEND_COLUMN:
2153         case LyXTabular::DELETE_ROW:
2154         case LyXTabular::DELETE_COLUMN:
2155         case LyXTabular::SET_ALL_LINES:
2156         case LyXTabular::UNSET_ALL_LINES:
2157                 return status.clear();
2158
2159         case LyXTabular::MULTICOLUMN:
2160                 status.setOnOff(tabular.isMultiColumn(actcell));
2161                 break;
2162
2163         case LyXTabular::M_TOGGLE_LINE_TOP:
2164                 flag = false;
2165         case LyXTabular::TOGGLE_LINE_TOP:
2166                 status.setOnOff(tabular.topLine(actcell, flag));
2167                 break;
2168
2169         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
2170                 flag = false;
2171         case LyXTabular::TOGGLE_LINE_BOTTOM:
2172                 status.setOnOff(tabular.bottomLine(actcell, flag));
2173                 break;
2174
2175         case LyXTabular::M_TOGGLE_LINE_LEFT:
2176                 flag = false;
2177         case LyXTabular::TOGGLE_LINE_LEFT:
2178                 status.setOnOff(tabular.leftLine(actcell, flag));
2179                 break;
2180
2181         case LyXTabular::M_TOGGLE_LINE_RIGHT:
2182                 flag = false;
2183         case LyXTabular::TOGGLE_LINE_RIGHT:
2184                 status.setOnOff(tabular.rightLine(actcell, flag));
2185                 break;
2186
2187         case LyXTabular::M_ALIGN_LEFT:
2188                 flag = false;
2189         case LyXTabular::ALIGN_LEFT:
2190                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_LEFT);
2191                 break;
2192
2193         case LyXTabular::M_ALIGN_RIGHT:
2194                 flag = false;
2195         case LyXTabular::ALIGN_RIGHT:
2196                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_RIGHT);
2197                 break;
2198
2199         case LyXTabular::M_ALIGN_CENTER:
2200                 flag = false;
2201         case LyXTabular::ALIGN_CENTER:
2202                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_CENTER);
2203                 break;
2204
2205         case LyXTabular::ALIGN_BLOCK:
2206                 status.disabled(tabular.getPWidth(actcell).zero());
2207                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_BLOCK);
2208                 break;
2209
2210         case LyXTabular::M_VALIGN_TOP:
2211                 flag = false;
2212         case LyXTabular::VALIGN_TOP:
2213                 status.setOnOff(tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_TOP);
2214                 break;
2215
2216         case LyXTabular::M_VALIGN_BOTTOM:
2217                 flag = false;
2218         case LyXTabular::VALIGN_BOTTOM:
2219                 status.setOnOff(tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_BOTTOM);
2220                 break;
2221
2222         case LyXTabular::M_VALIGN_CENTER:
2223                 flag = false;
2224         case LyXTabular::VALIGN_CENTER:
2225                 status.setOnOff(tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_CENTER);
2226                 break;
2227
2228         case LyXTabular::SET_LONGTABULAR:
2229                 status.setOnOff(tabular.isLongTabular());
2230                 break;
2231
2232         case LyXTabular::UNSET_LONGTABULAR:
2233                 status.setOnOff(!tabular.isLongTabular());
2234                 break;
2235
2236         case LyXTabular::SET_ROTATE_TABULAR:
2237                 status.setOnOff(tabular.getRotateTabular());
2238                 break;
2239
2240         case LyXTabular::UNSET_ROTATE_TABULAR:
2241                 status.setOnOff(!tabular.getRotateTabular());
2242                 break;
2243
2244         case LyXTabular::SET_ROTATE_CELL:
2245                 status.setOnOff(tabular.getRotateCell(actcell));
2246                 break;
2247
2248         case LyXTabular::UNSET_ROTATE_CELL:
2249                 status.setOnOff(!tabular.getRotateCell(actcell));
2250                 break;
2251
2252         case LyXTabular::SET_USEBOX:
2253                 status.setOnOff(strToInt(argument) == tabular.getUsebox(actcell));
2254                 break;
2255
2256         case LyXTabular::SET_LTFIRSTHEAD:
2257                 status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
2258                 break;
2259
2260         case LyXTabular::SET_LTHEAD:
2261                 status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
2262                 break;
2263
2264         case LyXTabular::SET_LTFOOT:
2265                 status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
2266                 break;
2267
2268         case LyXTabular::SET_LTLASTFOOT:
2269                 status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
2270                 break;
2271
2272         case LyXTabular::SET_LTNEWPAGE:
2273                 status.setOnOff(tabular.getLTNewPage(sel_row_start));
2274                 break;
2275
2276         default:
2277                 status.clear();
2278                 status.disabled(true);
2279                 break;
2280         }
2281         return status;
2282 }
2283
2284
2285 void InsetTabular::getLabelList(std::vector<string> & list) const
2286 {
2287         tabular.getLabelList(list);
2288 }
2289
2290
2291 bool InsetTabular::copySelection(BufferView * bv)
2292 {
2293         if (!hasSelection())
2294                 return false;
2295
2296         int sel_col_start = tabular.column_of_cell(sel_cell_start);
2297         int sel_col_end = tabular.column_of_cell(sel_cell_end);
2298         if (sel_col_start > sel_col_end) {
2299                 sel_col_start = sel_col_end;
2300                 sel_col_end = tabular.right_column_of_cell(sel_cell_start);
2301         } else {
2302                 sel_col_end = tabular.right_column_of_cell(sel_cell_end);
2303         }
2304
2305         int sel_row_start = tabular.row_of_cell(sel_cell_start);
2306         int sel_row_end = tabular.row_of_cell(sel_cell_end);
2307         if (sel_row_start > sel_row_end)
2308                 swap(sel_row_start, sel_row_end);
2309
2310         delete paste_tabular;
2311         paste_tabular = new LyXTabular(bv->buffer()->params, this, tabular);
2312
2313         for (int i = 0; i < sel_row_start; ++i)
2314                 paste_tabular->deleteRow(0);
2315
2316         int const rows = sel_row_end - sel_row_start + 1;
2317         while (paste_tabular->rows() > rows)
2318                 paste_tabular->deleteRow(rows);
2319
2320         paste_tabular->setTopLine(0, true, true);
2321         paste_tabular->setBottomLine(paste_tabular->getFirstCellInRow(rows - 1),
2322                                      true, true);
2323
2324         for (int i = 0; i < sel_col_start; ++i)
2325                 paste_tabular->deleteColumn(0);
2326
2327         int const columns = sel_col_end - sel_col_start + 1;
2328         while (paste_tabular->columns() > columns)
2329                 paste_tabular->deleteColumn(columns);
2330
2331         paste_tabular->setLeftLine(0, true, true);
2332         paste_tabular->setRightLine(paste_tabular->getLastCellInRow(0),
2333                                     true, true);
2334
2335         ostringstream os;
2336         paste_tabular->ascii(bv->buffer(), os,
2337                              (int)parOwner()->params().depth(), true, '\t');
2338         bv->stuffClipboard(STRCONV(os.str()));
2339         return true;
2340 }
2341
2342
2343 bool InsetTabular::pasteSelection(BufferView * bv)
2344 {
2345         if (!paste_tabular)
2346                 return false;
2347
2348         for (int r1 = 0, r2 = actrow;
2349              r1 < paste_tabular->rows() && r2 < tabular.rows();
2350              ++r1, ++r2) {
2351                 for (int c1 = 0, c2 = actcol;
2352                     c1 < paste_tabular->columns() && c2 < tabular.columns();
2353                     ++c1, ++c2) {
2354                         if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
2355                             tabular.isPartOfMultiColumn(r2, c2))
2356                                 continue;
2357                         if (paste_tabular->isPartOfMultiColumn(r1, c1)) {
2358                                 --c2;
2359                                 continue;
2360                         }
2361                         if (tabular.isPartOfMultiColumn(r2, c2)) {
2362                                 --c1;
2363                                 continue;
2364                         }
2365                         InsetText & inset = tabular.getCellInset(r2, c2);
2366                         inset = paste_tabular->getCellInset(r1, c1);
2367                         inset.setOwner(this);
2368                         inset.deleteLyXText(bv);
2369                         inset.markNew();
2370                 }
2371         }
2372         return true;
2373 }
2374
2375
2376 bool InsetTabular::cutSelection(BufferParams const & bp)
2377 {
2378         if (!hasSelection())
2379                 return false;
2380
2381         int sel_col_start = tabular.column_of_cell(sel_cell_start);
2382         int sel_col_end = tabular.column_of_cell(sel_cell_end);
2383         if (sel_col_start > sel_col_end) {
2384                 sel_col_start = sel_col_end;
2385                 sel_col_end = tabular.right_column_of_cell(sel_cell_start);
2386         } else {
2387                 sel_col_end = tabular.right_column_of_cell(sel_cell_end);
2388         }
2389
2390         int sel_row_start = tabular.row_of_cell(sel_cell_start);
2391         int sel_row_end = tabular.row_of_cell(sel_cell_end);
2392
2393         if (sel_row_start > sel_row_end)
2394                 swap(sel_row_start, sel_row_end);
2395
2396         if (sel_cell_start > sel_cell_end)
2397                 swap(sel_cell_start, sel_cell_end);
2398
2399         for (int i = sel_row_start; i <= sel_row_end; ++i)
2400                 for (int j = sel_col_start; j <= sel_col_end; ++j)
2401                         tabular.getCellInset(tabular.getCellNumber(i, j)).clear(bp.tracking_changes);
2402         return true;
2403 }
2404
2405
2406 bool InsetTabular::isRightToLeft(BufferView * bv)
2407 {
2408         return bv->getParentLanguage(this)->RightToLeft();
2409 }
2410
2411
2412 int InsetTabular::scroll(bool recursive) const
2413 {
2414         int sx = UpdatableInset::scroll(false);
2415
2416         if (recursive && the_locking_inset)
2417                 sx += the_locking_inset->scroll(recursive);
2418
2419         return sx;
2420 }
2421
2422
2423 void InsetTabular::getSelection(int & srow, int & erow,
2424                                 int & scol, int & ecol) const
2425 {
2426         int const start = hasSelection() ? sel_cell_start : actcell;
2427         int const end = hasSelection() ? sel_cell_end : actcell;
2428
2429         srow = tabular.row_of_cell(start);
2430         erow = tabular.row_of_cell(end);
2431         if (srow > erow)
2432                 swap(srow, erow);
2433
2434         scol = tabular.column_of_cell(start);
2435         ecol = tabular.column_of_cell(end);
2436         if (scol > ecol)
2437                 swap(scol, ecol);
2438         else
2439                 ecol = tabular.right_column_of_cell(end);
2440 }
2441
2442
2443 ParagraphList * InsetTabular::getParagraphs(int i) const
2444 {
2445         return (i < tabular.getNumberOfCells())
2446                 ? tabular.getCellInset(i).getParagraphs(0)
2447                 : 0;
2448 }
2449
2450
2451 LyXCursor const & InsetTabular::cursor(BufferView * bv) const
2452 {
2453         if (the_locking_inset)
2454                 return the_locking_inset->cursor(bv);
2455         return InsetOld::cursor(bv);
2456 }
2457
2458
2459 InsetOld * InsetTabular::getInsetFromID(int id_arg) const
2460 {
2461         if (id_arg == id())
2462                 return const_cast<InsetTabular *>(this);
2463
2464         for (int i = 0; i < tabular.rows(); ++i) {
2465                 for (int j = 0; j < tabular.columns(); ++j) {
2466                         InsetOld * inset = tabular.getCellInset(i, j).getInsetFromID(id_arg);
2467                         if (inset)
2468                                 return inset;
2469                 }
2470         }
2471         return 0;
2472 }
2473
2474
2475 WordLangTuple const
2476 InsetTabular::selectNextWordToSpellcheck(BufferView * bv, float & value) const
2477 {
2478         if (the_locking_inset) {
2479                 WordLangTuple word(the_locking_inset->selectNextWordToSpellcheck(bv, value));
2480                 if (!word.word().empty())
2481                         return word;
2482                 if (tabular.isLastCell(actcell)) {
2483                         bv->unlockInset(const_cast<InsetTabular *>(this));
2484                         return WordLangTuple();
2485                 }
2486                 ++actcell;
2487         }
2488         // otherwise we have to lock the next inset and ask for it's selecttion
2489         tabular.getCellInset(actcell)
2490                 .localDispatch(FuncRequest(bv, LFUN_INSET_EDIT));
2491         WordLangTuple word(selectNextWordInt(bv, value));
2492         if (!word.word().empty())
2493                 resetPos(bv);
2494         return word;
2495 }
2496
2497
2498 WordLangTuple InsetTabular::selectNextWordInt(BufferView * bv, float & value) const
2499 {
2500         // when entering this function the inset should be ALWAYS locked!
2501         Assert(the_locking_inset);
2502
2503         WordLangTuple word(the_locking_inset->selectNextWordToSpellcheck(bv, value));
2504         if (!word.word().empty())
2505                 return word;
2506
2507         if (tabular.isLastCell(actcell)) {
2508                 bv->unlockInset(const_cast<InsetTabular *>(this));
2509                 return WordLangTuple();
2510         }
2511
2512         // otherwise we have to lock the next inset and ask for it's selecttion
2513         ++actcell;
2514         tabular.getCellInset(actcell)
2515                 .localDispatch(FuncRequest(bv, LFUN_INSET_EDIT));
2516         return selectNextWordInt(bv, value);
2517 }
2518
2519
2520 void InsetTabular::selectSelectedWord(BufferView * bv)
2521 {
2522         if (the_locking_inset)
2523                 the_locking_inset->selectSelectedWord(bv);
2524 }
2525
2526
2527 void InsetTabular::markErased()
2528 {
2529         for (int cell = 0; cell < tabular.getNumberOfCells(); ++cell)
2530                 tabular.getCellInset(cell).markErased();
2531 }
2532
2533
2534 bool InsetTabular::nextChange(BufferView * bv, lyx::pos_type & length)
2535 {
2536         if (the_locking_inset) {
2537                 if (the_locking_inset->nextChange(bv, length)) {
2538                         updateLocal(bv, CELL);
2539                         return true;
2540                 }
2541                 if (tabular.isLastCell(actcell))
2542                         return false;
2543                 ++actcell;
2544         }
2545         InsetText & inset = tabular.getCellInset(actcell);
2546         if (inset.nextChange(bv, length)) {
2547                 updateLocal(bv, FULL);
2548                 return true;
2549         }
2550         while (!tabular.isLastCell(actcell)) {
2551                 ++actcell;
2552                 InsetText & inset = tabular.getCellInset(actcell);
2553                 if (inset.nextChange(bv, length)) {
2554                         updateLocal(bv, FULL);
2555                         return true;
2556                 }
2557         }
2558         return false;
2559 }
2560
2561
2562 bool InsetTabular::searchForward(BufferView * bv, string const & str,
2563                                  bool cs, bool mw)
2564 {
2565         int cell = 0;
2566         if (the_locking_inset) {
2567                 if (the_locking_inset->searchForward(bv, str, cs, mw)) {
2568                         updateLocal(bv, CELL);
2569                         return true;
2570                 }
2571                 if (tabular.isLastCell(actcell))
2572                         return false;
2573                 cell = actcell + 1;
2574         }
2575         InsetText & inset = tabular.getCellInset(cell);
2576         if (inset.searchForward(bv, str, cs, mw)) {
2577                 updateLocal(bv, FULL);
2578                 return true;
2579         }
2580         while (!tabular.isLastCell(cell)) {
2581                 ++cell;
2582                 InsetText & inset = tabular.getCellInset(cell);
2583                 if (inset.searchForward(bv, str, cs, mw)) {
2584                         updateLocal(bv, FULL);
2585                         return true;
2586                 }
2587         }
2588         return false;
2589 }
2590
2591
2592 bool InsetTabular::searchBackward(BufferView * bv, string const & str,
2593                                bool cs, bool mw)
2594 {
2595         int cell = tabular.getNumberOfCells();
2596         if (the_locking_inset) {
2597                 if (the_locking_inset->searchBackward(bv, str, cs, mw)) {
2598                         updateLocal(bv, CELL);
2599                         return true;
2600                 }
2601                 cell = actcell;
2602         }
2603
2604         while (cell) {
2605                 --cell;
2606                 InsetText & inset = tabular.getCellInset(cell);
2607                 if (inset.searchBackward(bv, str, cs, mw)) {
2608                         updateLocal(bv, CELL);
2609                         return true;
2610                 }
2611         }
2612         return false;
2613 }
2614
2615
2616 bool InsetTabular::insetAllowed(InsetOld::Code code) const
2617 {
2618         if (the_locking_inset)
2619                 return the_locking_inset->insetAllowed(code);
2620         // we return true here because if the inset is not locked someone
2621         // wants to insert something in one of our insettexts and we generally
2622         // allow to do so.
2623         return true;
2624 }
2625
2626
2627 bool InsetTabular::forceDefaultParagraphs(InsetOld const * in) const
2628 {
2629         const int cell = tabular.getCellFromInset(in, actcell);
2630
2631         if (cell != -1)
2632                 return tabular.getPWidth(cell).zero();
2633
2634         // well we didn't obviously find it so maybe our owner knows more
2635         if (owner())
2636                 return owner()->forceDefaultParagraphs(in);
2637
2638         // if we're here there is really something strange going on!!!
2639         lyxerr << "if we're here there is really something strange going on!\n";
2640         return false;
2641 }
2642
2643
2644 bool InsetTabular::insertAsciiString(BufferView * bv, string const & buf,
2645                                      bool usePaste)
2646 {
2647         if (buf.length() <= 0)
2648                 return true;
2649
2650         int cols = 1;
2651         int rows = 1;
2652         int maxCols = 1;
2653         string::size_type len = buf.length();
2654         string::size_type p = 0;
2655
2656         while (p < len && (p = buf.find_first_of("\t\n", p)) != string::npos) {
2657                 switch (buf[p]) {
2658                 case '\t':
2659                         ++cols;
2660                         break;
2661                 case '\n':
2662                         if ((p+1) < len)
2663                                 ++rows;
2664                         maxCols = max(cols, maxCols);
2665                         cols = 1;
2666                         break;
2667                 }
2668                 ++p;
2669         }
2670         maxCols = max(cols, maxCols);
2671         LyXTabular * loctab;
2672         int cell = 0;
2673         int ocol = 0;
2674         int row = 0;
2675         if (usePaste) {
2676                 delete paste_tabular;
2677                 paste_tabular = new LyXTabular(bv->buffer()->params,
2678                                                this, rows, maxCols);
2679                 loctab = paste_tabular;
2680                 cols = 0;
2681         } else {
2682                 loctab = &tabular;
2683                 cell = actcell;
2684                 ocol = actcol;
2685                 row = actrow;
2686         }
2687
2688         string::size_type op = 0;
2689         int cells = loctab->getNumberOfCells();
2690         p = 0;
2691         cols = ocol;
2692         rows = loctab->rows();
2693         int const columns = loctab->columns();
2694
2695         while (cell < cells && p < len && row < rows &&
2696                (p = buf.find_first_of("\t\n", p)) != string::npos)
2697         {
2698                 if (p >= len)
2699                         break;
2700                 switch (buf[p]) {
2701                 case '\t':
2702                         // we can only set this if we are not too far right
2703                         if (cols < columns) {
2704                                 InsetText & inset = loctab->getCellInset(cell);
2705                                 LyXFont const font = inset.getLyXText(bv)->
2706                                         getFont(inset.paragraphs.begin(), 0);
2707                                 inset.setText(buf.substr(op, p - op), font);
2708                                 ++cols;
2709                                 ++cell;
2710                         }
2711                         break;
2712                 case '\n':
2713                         // we can only set this if we are not too far right
2714                         if (cols < columns) {
2715                                 InsetText & inset = tabular.getCellInset(cell);
2716                                 LyXFont const font = inset.getLyXText(bv)->
2717                                         getFont(inset.paragraphs.begin(), 0);
2718                                 inset.setText(buf.substr(op, p - op), font);
2719                         }
2720                         cols = ocol;
2721                         ++row;
2722                         if (row < rows)
2723                                 cell = loctab->getCellNumber(row, cols);
2724                         break;
2725                 }
2726                 ++p;
2727                 op = p;
2728         }
2729         // check for the last cell if there is no trailing '\n'
2730         if (cell < cells && op < len) {
2731                 InsetText & inset = loctab->getCellInset(cell);
2732                 LyXFont const font = inset.getLyXText(bv)->
2733                         getFont(inset.paragraphs.begin(), 0);
2734                 inset.setText(buf.substr(op, len - op), font);
2735         }
2736
2737         return true;
2738 }
2739
2740
2741 void InsetTabular::addPreview(PreviewLoader & loader) const
2742 {
2743         int const rows = tabular.rows();
2744         int const columns = tabular.columns();
2745         for (int i = 0; i < rows; ++i)
2746                 for (int j = 0; j < columns; ++j)
2747                         tabular.getCellInset(i, j).addPreview(loader);
2748 }
2749
2750
2751 string const InsetTabularMailer::name_("tabular");
2752
2753 InsetTabularMailer::InsetTabularMailer(InsetTabular const & inset)
2754         : inset_(const_cast<InsetTabular &>(inset))
2755 {}
2756
2757
2758 string const InsetTabularMailer::inset2string(Buffer const &) const
2759 {
2760         return params2string(inset_);
2761 }
2762
2763
2764 int InsetTabularMailer::string2params(string const & in, InsetTabular & inset)
2765 {
2766         istringstream data(STRCONV(in));
2767         LyXLex lex(0,0);
2768         lex.setStream(data);
2769
2770 #warning CHECK verify that this is a sane value to return.
2771         if (in.empty())
2772                 return -1;
2773
2774         if (lex.isOK()) {
2775                 lex.next();
2776                 string const token = lex.getString();
2777                 if (token != name_)
2778                         return -1;
2779         }
2780
2781         int cell = -1;
2782         if (lex.isOK()) {
2783                 lex.next();
2784                 string const token = lex.getString();
2785                 if (token != "\\active_cell")
2786                         return -1;
2787                 lex.next();
2788                 cell = lex.getInteger();
2789         }
2790
2791         // This is part of the inset proper that is usually swallowed
2792         // by Buffer::readInset
2793         if (lex.isOK()) {
2794                 lex.next();
2795                 string const token = lex.getString();
2796                 if (token != "Tabular")
2797                         return -1;
2798         }
2799
2800         if (!lex.isOK())
2801                 return -1;
2802
2803         Buffer const * const buffer = inset.buffer();
2804         if (buffer)
2805                 inset.read(buffer, lex);
2806
2807         // We can't set the active cell, but we can tell the frontend
2808         // what it is.
2809         return cell;
2810 }
2811
2812
2813 string const InsetTabularMailer::params2string(InsetTabular const & inset)
2814 {
2815         Buffer const * const buffer = inset.buffer();
2816         if (!buffer)
2817                 return string();
2818
2819         ostringstream data;
2820         data << name_ << " \\active_cell " << inset.getActCell() << '\n';
2821         inset.write(buffer, data);
2822         data << "\\end_inset\n";
2823         return STRCONV(data.str());
2824 }