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