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