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