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