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