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