]> git.lyx.org Git - features.git/blob - src/insets/insettabular.C
some table work
[features.git] / src / insets / insettabular.C
1 /**
2  * \file insettabular.C
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Jürgen Vigna
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12
13 #include "insettabular.h"
14
15 #include "buffer.h"
16 #include "bufferparams.h"
17 #include "BufferView.h"
18 #include "cursor.h"
19 #include "debug.h"
20 #include "dispatchresult.h"
21 #include "funcrequest.h"
22 #include "FuncStatus.h"
23 #include "gettext.h"
24 #include "language.h"
25 #include "LColor.h"
26 #include "lyx_cb.h"
27 #include "lyxlex.h"
28 #include "metricsinfo.h"
29 #include "outputparams.h"
30 #include "paragraph.h"
31 #include "paragraph_funcs.h"
32 #include "ParagraphParameters.h"
33 #include "undo.h"
34
35 #include "frontends/Alert.h"
36 #include "frontends/font_metrics.h"
37 #include "frontends/LyXView.h"
38 #include "frontends/Painter.h"
39
40 #include "support/std_sstream.h"
41
42 #include <iostream>
43
44 using lyx::graphics::PreviewLoader;
45
46 using lyx::support::ltrim;
47 using lyx::support::strToInt;
48 using lyx::support::strToDbl;
49
50 using std::auto_ptr;
51 using std::endl;
52 using std::max;
53 using std::string;
54 using std::istringstream;
55 using std::ostream;
56 using std::ostringstream;
57 using std::swap;
58 using std::vector;
59
60
61 namespace {
62
63 int const ADD_TO_HEIGHT = 2;
64 int const ADD_TO_TABULAR_WIDTH = 2;
65
66 ///
67 boost::scoped_ptr<LyXTabular> paste_tabular;
68
69
70 struct TabularFeature {
71         LyXTabular::Feature action;
72         string feature;
73 };
74
75
76 TabularFeature tabularFeature[] =
77 {
78         { LyXTabular::APPEND_ROW, "append-row" },
79         { LyXTabular::APPEND_COLUMN, "append-column" },
80         { LyXTabular::DELETE_ROW, "delete-row" },
81         { LyXTabular::DELETE_COLUMN, "delete-column" },
82         { LyXTabular::TOGGLE_LINE_TOP, "toggle-line-top" },
83         { LyXTabular::TOGGLE_LINE_BOTTOM, "toggle-line-bottom" },
84         { LyXTabular::TOGGLE_LINE_LEFT, "toggle-line-left" },
85         { LyXTabular::TOGGLE_LINE_RIGHT, "toggle-line-right" },
86         { LyXTabular::ALIGN_LEFT, "align-left" },
87         { LyXTabular::ALIGN_RIGHT, "align-right" },
88         { LyXTabular::ALIGN_CENTER, "align-center" },
89         { LyXTabular::ALIGN_BLOCK, "align-block" },
90         { LyXTabular::VALIGN_TOP, "valign-top" },
91         { LyXTabular::VALIGN_BOTTOM, "valign-bottom" },
92         { LyXTabular::VALIGN_MIDDLE, "valign-middle" },
93         { LyXTabular::M_TOGGLE_LINE_TOP, "m-toggle-line-top" },
94         { LyXTabular::M_TOGGLE_LINE_BOTTOM, "m-toggle-line-bottom" },
95         { LyXTabular::M_TOGGLE_LINE_LEFT, "m-toggle-line-left" },
96         { LyXTabular::M_TOGGLE_LINE_RIGHT, "m-toggle-line-right" },
97         { LyXTabular::M_ALIGN_LEFT, "m-align-left" },
98         { LyXTabular::M_ALIGN_RIGHT, "m-align-right" },
99         { LyXTabular::M_ALIGN_CENTER, "m-align-center" },
100         { LyXTabular::M_VALIGN_TOP, "m-valign-top" },
101         { LyXTabular::M_VALIGN_BOTTOM, "m-valign-bottom" },
102         { LyXTabular::M_VALIGN_MIDDLE, "m-valign-middle" },
103         { LyXTabular::MULTICOLUMN, "multicolumn" },
104         { LyXTabular::SET_ALL_LINES, "set-all-lines" },
105         { LyXTabular::UNSET_ALL_LINES, "unset-all-lines" },
106         { LyXTabular::SET_LONGTABULAR, "set-longtabular" },
107         { LyXTabular::UNSET_LONGTABULAR, "unset-longtabular" },
108         { LyXTabular::SET_PWIDTH, "set-pwidth" },
109         { LyXTabular::SET_MPWIDTH, "set-mpwidth" },
110         { LyXTabular::SET_ROTATE_TABULAR, "set-rotate-tabular" },
111         { LyXTabular::UNSET_ROTATE_TABULAR, "unset-rotate-tabular" },
112         { LyXTabular::SET_ROTATE_CELL, "set-rotate-cell" },
113         { LyXTabular::UNSET_ROTATE_CELL, "unset-rotate-cell" },
114         { LyXTabular::SET_USEBOX, "set-usebox" },
115         { LyXTabular::SET_LTHEAD, "set-lthead" },
116         { LyXTabular::SET_LTFIRSTHEAD, "set-ltfirsthead" },
117         { LyXTabular::SET_LTFOOT, "set-ltfoot" },
118         { LyXTabular::SET_LTLASTFOOT, "set-ltlastfoot" },
119         { LyXTabular::SET_LTNEWPAGE, "set-ltnewpage" },
120         { LyXTabular::SET_SPECIAL_COLUMN, "set-special-column" },
121         { LyXTabular::SET_SPECIAL_MULTI, "set-special-multi" },
122         { LyXTabular::LAST_ACTION, "" }
123 };
124
125
126 class FeatureEqual : public std::unary_function<TabularFeature, bool> {
127 public:
128         FeatureEqual(LyXTabular::Feature feature)
129                 : feature_(feature) {}
130         bool operator()(TabularFeature const & tf) const {
131                 return tf.action == feature_;
132         }
133 private:
134         LyXTabular::Feature feature_;
135 };
136
137 } // namespace anon
138
139
140 string const featureAsString(LyXTabular::Feature feature)
141 {
142         TabularFeature * end = tabularFeature +
143                 sizeof(tabularFeature) / sizeof(TabularFeature);
144         TabularFeature * it = std::find_if(tabularFeature, end,
145                                            FeatureEqual(feature));
146         return (it == end) ? string() : it->feature;
147 }
148
149
150 bool InsetTabular::hasPasteBuffer() const
151 {
152         return (paste_tabular.get() != 0);
153 }
154
155
156 InsetTabular::InsetTabular(Buffer const & buf, int rows, int columns)
157         : tabular(buf.params(), max(rows, 1), max(columns, 1)),
158           buffer_(&buf), cursorx_(0), cursory_(0)
159 {
160         tabular.setOwner(this);
161         in_reset_pos = 0;
162 }
163
164
165 InsetTabular::InsetTabular(InsetTabular const & tab)
166         : UpdatableInset(tab), tabular(tab.tabular),
167                 buffer_(tab.buffer_), cursorx_(0), cursory_(0)
168 {
169         tabular.setOwner(this);
170         in_reset_pos = 0;
171 }
172
173
174 InsetTabular::~InsetTabular()
175 {
176         InsetTabularMailer(*this).hideDialog();
177 }
178
179
180 auto_ptr<InsetBase> InsetTabular::clone() const
181 {
182         return auto_ptr<InsetBase>(new InsetTabular(*this));
183 }
184
185
186 Buffer const & InsetTabular::buffer() const
187 {
188         return *buffer_;
189 }
190
191
192 void InsetTabular::buffer(Buffer * b)
193 {
194         buffer_ = b;
195 }
196
197
198 void InsetTabular::write(Buffer const & buf, ostream & os) const
199 {
200         os << "Tabular" << endl;
201         tabular.write(buf, os);
202 }
203
204
205 void InsetTabular::read(Buffer const & buf, LyXLex & lex)
206 {
207         bool const old_format = (lex.getString() == "\\LyXTable");
208
209         tabular.read(buf, lex);
210
211         if (old_format)
212                 return;
213
214         lex.nextToken();
215         string token = lex.getString();
216         while (lex.isOK() && (token != "\\end_inset")) {
217                 lex.nextToken();
218                 token = lex.getString();
219         }
220         if (token != "\\end_inset") {
221                 lex.printError("Missing \\end_inset at this point. "
222                                "Read: `$$Token'");
223         }
224 }
225
226
227 void InsetTabular::metrics(MetricsInfo & mi, Dimension & dim) const
228 {
229         //lyxerr << "InsetTabular::metrics: " << mi.base.bv << " width: " <<
230         //      mi.base.textwidth << "\n";
231         if (!mi.base.bv) {
232                 lyxerr << "InsetTabular::metrics: need bv" << endl;
233                 BOOST_ASSERT(false);
234         }
235
236         calculate_dimensions_of_cells(mi);
237
238         dim.asc = tabular.getAscentOfRow(0);
239         dim.des = tabular.getHeightOfTabular() - tabular.getAscentOfRow(0) + 1;
240         dim.wid = tabular.getWidthOfTabular() + 2 * ADD_TO_TABULAR_WIDTH;
241         dim_ = dim;
242 }
243
244
245 void InsetTabular::draw(PainterInfo & pi, int x, int y) const
246 {
247         //lyxerr << "InsetTabular::draw: " << x << " " << y << endl;
248
249         BufferView * bv = pi.base.bv;
250         setPosCache(pi, x, y);
251
252         if (!owner())
253                 x += scroll();
254
255         x += ADD_TO_TABULAR_WIDTH;
256
257         int cell = 0;
258         first_visible_cell = -1;
259         for (int i = 0; i < tabular.rows(); ++i) {
260                 int nx = x;
261                 cell = tabular.getCellNumber(i, 0);
262                 if (y + tabular.getDescentOfRow(i) <= 0 &&
263                           y - tabular.getAscentOfRow(i) < pi.pain.paperHeight())
264                 {
265                         y += tabular.getDescentOfRow(i) +
266                                         tabular.getAscentOfRow(i + 1) +
267                                         tabular.getAdditionalHeight(i + 1);
268                         continue;
269                 }
270                 for (int j = 0; j < tabular.columns(); ++j) {
271                         if (nx > bv->workWidth())
272                                 break;
273                         if (tabular.isPartOfMultiColumn(i, j))
274                                 continue;
275                         if (first_visible_cell < 0)
276                                 first_visible_cell = cell;
277                         if (bv->cursor().selection())
278                                 drawCellSelection(pi, nx, y, i, j, cell);
279
280                         int const cx = nx + tabular.getBeginningOfTextInCell(cell);
281                         tabular.getCellInset(cell).draw(pi, cx, y);
282                         drawCellLines(pi.pain, nx, y, i, cell);
283                         nx += tabular.getWidthOfColumn(cell);
284                         ++cell;
285                 }
286
287 // Would be nice, but for some completely unfathomable reason,
288 // on a col resize to a new fixed width, even though the insettexts
289 // are resized, the cell isn't, but drawing all cells in a tall table
290 // has the desired effect somehow. Complete dark magic.
291 #if 0
292                 // avoiding drawing the rest of a long table is
293                 // a pretty big speedup
294                 if (y > bv->workHeight())
295                         break;
296 #endif
297
298                 y += tabular.getDescentOfRow(i) +
299                         tabular.getAscentOfRow(i + 1) +
300                         tabular.getAdditionalHeight(i + 1);
301         }
302 }
303
304
305 void InsetTabular::drawCellLines(Painter & pain, int x, int y,
306                                  int row, int cell) const
307 {
308         int x2 = x + tabular.getWidthOfColumn(cell);
309         bool on_off = false;
310
311         if (!tabular.topAlreadyDrawn(cell)) {
312                 on_off = !tabular.topLine(cell);
313                 pain.line(x, y - tabular.getAscentOfRow(row),
314                           x2, y -  tabular.getAscentOfRow(row),
315                           on_off ? LColor::tabularonoffline : LColor::tabularline,
316                           on_off ? Painter::line_onoffdash : Painter::line_solid);
317         }
318         on_off = !tabular.bottomLine(cell);
319         pain.line(x, y + tabular.getDescentOfRow(row),
320                   x2, y + tabular.getDescentOfRow(row),
321                   on_off ? LColor::tabularonoffline : LColor::tabularline,
322                   on_off ? Painter::line_onoffdash : Painter::line_solid);
323         if (!tabular.leftAlreadyDrawn(cell)) {
324                 on_off = !tabular.leftLine(cell);
325                 pain.line(x, y -  tabular.getAscentOfRow(row),
326                           x, y +  tabular.getDescentOfRow(row),
327                           on_off ? LColor::tabularonoffline : LColor::tabularline,
328                           on_off ? Painter::line_onoffdash : Painter::line_solid);
329         }
330         on_off = !tabular.rightLine(cell);
331         pain.line(x2 - tabular.getAdditionalWidth(cell),
332                   y -  tabular.getAscentOfRow(row),
333                   x2 - tabular.getAdditionalWidth(cell),
334                   y +  tabular.getDescentOfRow(row),
335                   on_off ? LColor::tabularonoffline : LColor::tabularline,
336                   on_off ? Painter::line_onoffdash : Painter::line_solid);
337 }
338
339
340 void InsetTabular::drawCellSelection(PainterInfo & pi, int x, int y,
341                                      int row, int column, int cell) const
342 {
343         LCursor & cur = pi.base.bv->cursor();
344         BOOST_ASSERT(cur.selection());
345         if (tablemode(cur)) {
346                 int rs, re, cs, ce;
347                 getSelection(cur, rs, re, cs, ce);
348                 if (column >= cs && column <= ce && row >= rs && row <= re) {
349                         int w = tabular.getWidthOfColumn(cell);
350                         int h = tabular.getAscentOfRow(row) + tabular.getDescentOfRow(row)-1;
351                         pi.pain.fillRectangle(x, y - tabular.getAscentOfRow(row) + 1,
352                                                  w, h, LColor::selection);
353                 }
354         }
355 }
356
357
358 string const InsetTabular::editMessage() const
359 {
360         return _("Opened table");
361 }
362
363
364 void InsetTabular::updateLocal(LCursor & cur) const
365 {
366         cur.bv().update();
367         resetPos(cur);
368 }
369
370
371 void InsetTabular::lfunMousePress(LCursor & cur, FuncRequest const & cmd)
372 {
373         if (cmd.button() == mouse_button::button3)
374                 return;
375
376         int cell = getCell(cmd.x + xo_, cmd.y + yo_);
377         cur.selection() = false;
378
379         lyxerr << "# InsetTabular::lfunMousePress cell: " << cell << endl;
380         if (cell == -1) {
381                 //cur.cursor_ = theTempCursor;
382                 cur.push(this);
383                 cur.idx() = cell;
384         } else {
385                 setPos(cur, cmd.x, cmd.y);
386                 //cur.cursor_ = theTempCursor;
387                 cur.idx() = cell;
388         }
389         cur.resetAnchor();
390         lyxerr << cur << endl;
391
392         if (cmd.button() == mouse_button::button2)
393                 dispatch(cur, FuncRequest(LFUN_PASTESELECTION, "paragraph"));
394 }
395
396
397 void InsetTabular::lfunMouseRelease(LCursor & cur, FuncRequest const & cmd)
398 {
399         int const actcell = getCell(cmd.x + xo_, cmd.y + yo_);
400         lyxerr << "# InsetTabular::lfunMouseRelease cell: " << actcell << endl;
401         if (cmd.button() == mouse_button::button3)
402                 InsetTabularMailer(*this).showDialog(&cur.bv());
403 }
404
405
406 void InsetTabular::edit(LCursor & cur, bool left)
407 {
408         lyxerr << "InsetTabular::edit: " << this << endl;
409         finishUndo();
410         int cell;
411         if (left) {
412                 if (isRightToLeft(cur))
413                         cell = tabular.getLastCellInRow(0);
414                 else
415                         cell = 0;
416         } else {
417                 if (isRightToLeft(cur))
418                         cell = tabular.getFirstCellInRow(tabular.rows()-1);
419                 else
420                         cell = tabular.getNumberOfCells() - 1;
421         }
422         cur.selection() = false;
423         resetPos(cur);
424         cur.bv().fitCursor();
425         cur.push(this);
426         cur.idx() = cell;
427 }
428
429
430 InsetBase * InsetTabular::editXY(LCursor & cur, int x, int y)
431 {
432         lyxerr << "InsetTabular::edit: " << this << endl;
433         cur.selection() = false;
434         cur.push(this);
435         return setPos(cur, x, y);
436         //int xx = cursorx_ - xo_ + tabular.getBeginningOfTextInCell(actcell);
437         //if (x > xx)
438         //      activateCellInset(bv, cell, x - xx, y - cursory_);
439 }
440
441
442 void InsetTabular::lfunMouseMotion(LCursor & cur, FuncRequest const & cmd)
443 {
444         int const actcell = getCell(cmd.x + xo_, cmd.y + yo_);
445         cur.idx() = actcell;
446         lyxerr << "# InsetTabular::lfunMouseMotion cell: " << actcell << endl;
447
448         setPos(cur, cmd.x, cmd.y);
449 /*
450         if (!hasSelection()) {
451                 setSelection(actcell, actcell);
452                 cur.setSelection();
453         } else {
454                 setSelection(sel_cell_start, actcell);
455                 tablemode = (sel_cell_start != actcell);
456         }
457 */
458 }
459
460
461 DispatchResult
462 InsetTabular::priv_dispatch(LCursor & cur, FuncRequest const & cmd)
463 {
464         lyxerr << "# InsetTabular::dispatch: " << cmd << endl;
465
466         InsetText & cell = tabular.getCellInset(cur.idx());
467
468         DispatchResult result(true, true);
469         switch (cmd.action) {
470
471         case LFUN_MOUSE_PRESS:
472                 if (cmd.button() == mouse_button::button3)
473                         DispatchResult(true, true);
474
475         case LFUN_MOUSE_MOTION:
476                 lfunMouseMotion(cur, cmd);
477                 return DispatchResult(true, true);
478
479         case LFUN_MOUSE_RELEASE:
480                 lfunMouseRelease(cur, cmd);
481                 return DispatchResult(true, true);
482
483 #if 0
484         int const cell = cur.idx();
485         lyxerr << "# InsetTabular::dispatch: A " << cur << endl;
486         result = cell.dispatch(cur, cmd);
487
488         switch (result.val()) {
489         case FINISHED:
490                 if (movePrevCell(cur))
491                         result = DispatchResult(true, true);
492                 else
493                         result = DispatchResult(false, FINISHED);
494                 break;
495
496         case FINISHED_RIGHT:
497                 if (moveNextCell(cur))
498                         result = DispatchResult(true, true);
499                 else
500                         result = DispatchResult(false, FINISHED_RIGHT);
501                 break;
502
503         case FINISHED_UP:
504                 if (moveUpLock(cur))
505                         result = DispatchResult(true, true);
506                 else
507                         result = DispatchResult(false, FINISHED_UP);
508                 break;
509
510         case FINISHED_DOWN:
511                 if (moveDownLock(cur))
512                         result = DispatchResult(true, true);
513                 else
514                         result = DispatchResult(false, FINISHED_UP);
515                 break;
516 #endif
517
518         case LFUN_CELL_BACKWARD:
519         case LFUN_CELL_FORWARD:
520                 if (cmd.action == LFUN_CELL_FORWARD)
521                         moveNextCell(cur);
522                 else
523                         movePrevCell(cur);
524                 cur.selection() = false;
525                 return result;
526
527         case LFUN_SCROLL_INSET:
528                 if (!cmd.argument.empty()) {
529                         if (cmd.argument.find('.') != cmd.argument.npos)
530                                 scroll(cur.bv(), static_cast<float>(strToDbl(cmd.argument)));
531                         else
532                                 scroll(cur.bv(), strToInt(cmd.argument));
533                         cur.update();
534                         return DispatchResult(true, true);
535                 }
536
537         case LFUN_RIGHTSEL:
538         case LFUN_RIGHT: {
539                 CursorSlice sl = cur.current();
540                 cell.dispatch(cur, cmd);
541                 if (sl == cur.current())
542                         isRightToLeft(cur) ? movePrevCell(cur) : moveNextCell(cur);
543                 if (sl == cur.current())
544                         result = DispatchResult(true, true);
545                 break;
546         }
547
548         case LFUN_LEFTSEL: 
549         case LFUN_LEFT: {
550                 CursorSlice sl = cur.current();
551                 cell.dispatch(cur, cmd);
552                 if (sl == cur.current())
553                         isRightToLeft(cur) ? moveNextCell(cur) : movePrevCell(cur);
554                 if (sl == cur.current())
555                         result = DispatchResult(true, true);
556                 break;
557         }
558
559 #if 0
560         case LFUN_DOWNSEL: {
561                 int const start = hasSelection() ? sel_cell_start : cur.idx();
562                 int const ocell = cur.idx();
563                 // if we are starting a selection, only select
564                 // the current cell at the beginning
565                 if (hasSelection()) {
566                         moveDown(cur);
567                         if (ocell == sel_cell_end ||
568                                         tabular.column_of_cell(ocell) >
569 tabular.column_of_cell(cur.idx()))
570                                 //setSelection(start, tabular.getCellBelow(sel_cell_end));
571                         else
572                                 //setSelection(start, tabular.getLastCellBelow(sel_cell_end));
573                 } else {
574                         //setSelection(start, start);
575                 }
576                 break;
577         }
578 #endif
579
580         case LFUN_DOWN:
581                 if (!moveDown(cur))
582                         result = DispatchResult(false, FINISHED_DOWN);
583                 cur.selection() = false;
584                 break;
585
586 #if 0
587         case LFUN_UPSEL: {
588                 int const start = hasSelection() ? sel_cell_start : cur.idx();
589                 int const ocell = cur.idx();
590                 // if we are starting a selection, only select
591                 // the current cell at the beginning
592                 if (hasSelection()) {
593                         moveUp(cur);
594                         if (ocell == sel_cell_end ||
595                                         tabular.column_of_cell(ocell) >
596 tabular.column_of_cell(cur.idx()))
597                                 //setSelection(start, tabular.getCellAbove(sel_cell_end));
598                         else
599                                 //setSelection(start, tabular.getLastCellAbove(sel_cell_end));
600                 } else {
601                         //setSelection(start, start);
602                 }
603                 break;
604         }
605 #endif
606
607         case LFUN_UP:
608                 if (!moveUp(cur))
609                         result = DispatchResult(false, FINISHED_DOWN);
610                 cur.selection() = false;
611                 break;
612
613         case LFUN_NEXT: {
614                 //if (hasSelection())
615                 //      cur.selection() = false;
616                 int actcell = cur.idx();
617                 int actcol = tabular.column_of_cell(actcell);
618                 int column = actcol;
619                 if (cur.bv().top_y() + cur.bv().painter().paperHeight()
620                                 < yo_ + tabular.getHeightOfTabular())
621                 {
622                         cur.bv().scrollDocView(
623                                 cur.bv().top_y() + cur.bv().painter().paperHeight());
624                         cur.idx() = tabular.getCellBelow(first_visible_cell) + column;
625                 } else {
626                         cur.idx() = tabular.getFirstCellInRow(tabular.rows() - 1) + column;
627                 }
628                 resetPos(cur);
629                 break;
630         }
631
632         case LFUN_PRIOR: {
633                 //if (hasSelection())
634                 //      cur.selection() = false;
635                 int column = tabular.column_of_cell(cur.idx());
636                 if (yo_ < 0) {
637                         cur.bv().scrollDocView(
638                                 cur.bv().top_y() - cur.bv().painter().paperHeight());
639                         if (yo_ > 0)
640                                 cur.idx() = column;
641                         else
642                                 cur.idx() = tabular.getCellBelow(first_visible_cell) + column;
643                 } else {
644                         cur.idx() = column;
645                 }
646                 resetPos(cur);
647                 break;
648         }
649
650         case LFUN_LAYOUT_TABULAR:
651                 InsetTabularMailer(*this).showDialog(&cur.bv());
652                 break;
653
654         case LFUN_INSET_DIALOG_UPDATE:
655                 InsetTabularMailer(*this).updateDialog(&cur.bv());
656                 break;
657
658         case LFUN_TABULAR_FEATURE:
659                 if (!tabularFeatures(cur, cmd.argument))
660                         result = DispatchResult(false);
661                 break;
662
663         // insert file functions
664         case LFUN_FILE_INSERT_ASCII_PARA:
665         case LFUN_FILE_INSERT_ASCII: {
666                 string tmpstr = getContentsOfAsciiFile(&cur.bv(), cmd.argument, false);
667                 if (!tmpstr.empty() && !insertAsciiString(cur.bv(), tmpstr, false))
668                         result = DispatchResult(false);
669                 break;
670         }
671
672         case LFUN_LANGUAGE:
673         case LFUN_EMPH:
674         case LFUN_BOLD:
675         case LFUN_NOUN:
676         case LFUN_CODE:
677         case LFUN_SANS:
678         case LFUN_ROMAN:
679         case LFUN_DEFAULT:
680         case LFUN_UNDERLINE:
681         case LFUN_FONT_SIZE:
682                 lyxerr << "font changes not re-implemented for tables after LOCK" << endl;
683                 break;
684
685         case LFUN_CUT:
686                 if (copySelection(cur)) {
687                         recordUndo(cur, Undo::DELETE);
688                         cutSelection(cur);
689                 }
690                 break;
691
692         case LFUN_BACKSPACE:
693         case LFUN_DELETE:
694                 recordUndo(cur, Undo::DELETE);
695                 if (tablemode(cur))
696                         cutSelection(cur);
697                 else
698                         cell.dispatch(cur, cmd);
699                 break;
700
701         case LFUN_COPY:
702                 if (!cur.selection())
703                         break;
704                 finishUndo();
705                 copySelection(cur);
706                 break;
707
708         case LFUN_PASTESELECTION: {
709                 string const clip = cur.bv().getClipboard();
710                 if (clip.empty())
711                         break;
712                 if (clip.find('\t') != string::npos) {
713                         int cols = 1;
714                         int rows = 1;
715                         int maxCols = 1;
716                         string::size_type len = clip.length();
717                         string::size_type p = 0;
718
719                         while (p < len &&
720                                                 (p = clip.find_first_of("\t\n", p)) != string::npos) {
721                                 switch (clip[p]) {
722                                 case '\t':
723                                         ++cols;
724                                         break;
725                                 case '\n':
726                                         if (p + 1 < len)
727                                                 ++rows;
728                                         maxCols = max(cols, maxCols);
729                                         cols = 1;
730                                         break;
731                                 }
732                                 ++p;
733                         }
734                         maxCols = max(cols, maxCols);
735
736                         paste_tabular.reset(
737                                 new LyXTabular(cur.bv().buffer()->params(), rows, maxCols));
738
739                         string::size_type op = 0;
740                         int cell = 0;
741                         int cells = paste_tabular->getNumberOfCells();
742                         p = 0;
743                         cols = 0;
744                         LyXFont font;
745                         while (cell < cells && p < len &&
746                                                 (p = clip.find_first_of("\t\n", p)) != string::npos) {
747                                 if (p >= len)
748                                         break;
749                                 switch (clip[p]) {
750                                 case '\t':
751                                         paste_tabular->getCellInset(cell).
752                                                 setText(clip.substr(op, p-op), font);
753                                         ++cols;
754                                         ++cell;
755                                         break;
756                                 case '\n':
757                                         paste_tabular->getCellInset(cell).
758                                                 setText(clip.substr(op, p-op), font);
759                                         while (cols++ < maxCols)
760                                                 ++cell;
761                                         cols = 0;
762                                         break;
763                                 }
764                                 ++p;
765                                 op = p;
766                         }
767                         // check for the last cell if there is no trailing '\n'
768                         if (cell < cells && op < len)
769                                 paste_tabular->getCellInset(cell).
770                                         setText(clip.substr(op, len-op), font);
771                 } else if (!insertAsciiString(cur.bv(), clip, true))
772                 {
773                         // so that the clipboard is used and it goes on
774                         // to default
775                         // and executes LFUN_PASTESELECTION in insettext!
776                         paste_tabular.reset();
777                 }
778                 // fall through
779         }
780
781         case LFUN_PASTE:
782                 if (hasPasteBuffer()) {
783                         recordUndo(cur, Undo::INSERT);
784                         pasteSelection(cur);
785                         break;
786                 }
787                 // fall through
788
789         // ATTENTION: the function above has to be PASTE and PASTESELECTION!!!
790
791         default:
792                 // handle font changing stuff on selection before we lock the inset
793                 // in the default part!
794                 result = cell.dispatch(cur, cmd);
795                 // we try to activate the actual inset and put this event down to
796                 // the insets dispatch function.
797                 break;
798         }
799
800         updateLocal(cur);
801         InsetTabularMailer(*this).updateDialog(&cur.bv());
802
803         return result;
804 }
805
806
807 int InsetTabular::latex(Buffer const & buf, ostream & os,
808                         OutputParams const & runparams) const
809 {
810         return tabular.latex(buf, os, runparams);
811 }
812
813
814 int InsetTabular::plaintext(Buffer const & buf, ostream & os,
815                         OutputParams const & runparams) const
816 {
817         int dp = runparams.linelen ? ownerPar(buf, this).params().depth() : 0;
818         return tabular.plaintext(buf, os, runparams, dp, false, 0);
819 }
820
821
822 int InsetTabular::linuxdoc(Buffer const & buf, ostream & os,
823                            OutputParams const & runparams) const
824 {
825         return tabular.linuxdoc(buf,os, runparams);
826 }
827
828
829 int InsetTabular::docbook(Buffer const & buf, ostream & os,
830                           OutputParams const & runparams) const
831 {
832         int ret = 0;
833         InsetOld * master;
834
835         // if the table is inside a float it doesn't need the informaltable
836         // wrapper. Search for it.
837         for (master = owner(); master; master = master->owner())
838                 if (master->lyxCode() == InsetOld::FLOAT_CODE)
839                         break;
840
841         if (!master) {
842                 os << "<informaltable>";
843                 if (runparams.mixed_content)
844                         os << endl;
845                 ++ret;
846         }
847         ret += tabular.docbook(buf, os, runparams);
848         if (!master) {
849                 os << "</informaltable>";
850                 if (runparams.mixed_content)
851                         os << endl;
852                 ++ret;
853         }
854         return ret;
855 }
856
857
858 void InsetTabular::validate(LaTeXFeatures & features) const
859 {
860         tabular.validate(features);
861 }
862
863
864 void InsetTabular::calculate_dimensions_of_cells(MetricsInfo & mi) const
865 {
866         for (int i = 0, cell = -1; i < tabular.rows(); ++i) {
867                 int maxAsc = 0;
868                 int maxDesc = 0;
869                 for (int j = 0; j < tabular.columns(); ++j) {
870                         if (tabular.isPartOfMultiColumn(i, j))
871                                 continue;
872                         ++cell;
873                         Dimension dim;
874                         MetricsInfo m = mi;
875                         m.base.textwidth =
876                                 tabular.column_info[j].p_width.inPixels(mi.base.textwidth);
877                         tabular.getCellInset(cell).metrics(m, dim);
878                         maxAsc  = max(maxAsc, dim.asc);
879                         maxDesc = max(maxDesc, dim.des);
880                         tabular.setWidthOfCell(cell, dim.wid);
881                 }
882                 tabular.setAscentOfRow(i, maxAsc + ADD_TO_HEIGHT);
883                 tabular.setDescentOfRow(i, maxDesc + ADD_TO_HEIGHT);
884         }
885 }
886
887
888 void InsetTabular::getCursorPos(CursorSlice const & cur, int & x, int & y) const
889 {
890         tabular.getCellInset(cur.idx()).getCursorPos(cur, x, y);
891 }
892
893
894 InsetBase * InsetTabular::setPos(LCursor & cur, int x, int y) const
895 {
896         cur.idx() = getCell(x, y);
897         InsetBase * inset = tabular.getCellInset(cur.idx()).text_.editXY(cur, x, y);
898         lyxerr << "# InsetTabular::setPos()  cursor: " << cur << endl;
899         return inset;
900 }
901
902
903 int InsetTabular::getCellXPos(int cell) const
904 {
905         int c = cell;
906
907         for (; !tabular.isFirstCellInRow(c); --c)
908                 ;
909         int lx = tabular.getWidthOfColumn(cell);
910         for (; c < cell; ++c)
911                 lx += tabular.getWidthOfColumn(c);
912
913         return lx - tabular.getWidthOfColumn(cell) + xo_;
914 }
915
916
917 void InsetTabular::resetPos(LCursor &) const
918 {
919 #if 0
920 #ifdef WITH_WARNINGS
921 #warning This should be fixed in the right manner (20011128 Jug)
922 #endif
923         // fast hack to fix infinite repaintings!
924         if (in_reset_pos > 0)
925                 return;
926
927         BufferView & bv = cur.bv();
928         int cell = 0;
929         int actcell = cur.idx();
930         int actcol = tabular.column_of_cell(actcell);
931         int actrow = 0;
932         cursory_ = 0;
933         for (; cell < actcell && !tabular.isLastRow(cell); ++cell) {
934                 if (tabular.isLastCellInRow(cell)) {
935                         cursory_ += tabular.getDescentOfRow(actrow) +
936                                          tabular.getAscentOfRow(actrow + 1) +
937                                          tabular.getAdditionalHeight(actrow + 1);
938                         ++actrow;
939                 }
940         }
941
942         // we need this only from here on!!!
943         ++in_reset_pos;
944         int const offset = ADD_TO_TABULAR_WIDTH + 2;
945         int new_x = getCellXPos(actcell) + offset;
946         int old_x = cursorx_;
947         cursorx_ = new_x;
948 //    cursor.x(getCellXPos(actcell) + offset);
949         if (actcol < tabular.columns() - 1 && scroll(false) &&
950                 tabular.getWidthOfTabular() < bv.workWidth()-20)
951         {
952                 scroll(bv, 0.0F);
953                 updateLocal(cur);
954         } else if (cursorx_ - offset > 20 &&
955                    cursorx_ - offset + tabular.getWidthOfColumn(actcell)
956                    > bv.workWidth() - 20) {
957                 scroll(bv, - tabular.getWidthOfColumn(actcell) - 20);
958                 updateLocal(cur);
959         } else if (cursorx_ - offset < 20) {
960                 scroll(bv, 20 - cursorx_ + offset);
961                 updateLocal(cur);
962         } else if (scroll() && xo_ > 20 &&
963                    xo_ + tabular.getWidthOfTabular() > bv.workWidth() - 20) {
964                 scroll(bv, old_x - cursorx_);
965                 updateLocal(cur);
966         }
967         InsetTabularMailer(*this).updateDialog(&bv);
968         in_reset_pos = 0;
969 #endif
970 }
971
972
973 bool InsetTabular::moveRight(LCursor & cur)
974 {
975         bool moved = isRightToLeft(cur) ? movePrevCell(cur) : moveNextCell(cur);
976         if (!moved)
977                 return false;
978         resetPos(cur);
979         return true;
980 }
981
982
983 bool InsetTabular::moveLeft(LCursor & cur)
984 {
985         bool moved = isRightToLeft(cur) ? moveNextCell(cur) : movePrevCell(cur);
986         if (!moved)
987                 return false;
988         resetPos(cur);
989         return true;
990 }
991
992
993 bool InsetTabular::moveLeftLock(LCursor & cur)
994 {
995         bool moved = isRightToLeft(cur) ? moveNextCell(cur) : movePrevCell(cur);
996         if (!moved)
997                 return false;
998         activateCellInset(cur, cur.idx(), true);
999         return true;
1000 }
1001
1002
1003 bool InsetTabular::moveUp(LCursor & cur)
1004 {
1005         if (tabular.row_of_cell(cur.idx()) == 0)
1006                 return false;
1007         cur.idx() = tabular.getCellAbove(cur.idx());
1008         resetPos(cur);
1009         return true;
1010 }
1011
1012
1013 bool InsetTabular::moveUpLock(LCursor & cur)
1014 {
1015         if (tabular.row_of_cell(cur.idx()) == 0)
1016                 return false;
1017         cur.idx() = tabular.getCellAbove(cur.idx());
1018         resetPos(cur);
1019         activateCellInset(cur, cur.idx(), cur.x_target(), 0);
1020         return true;
1021 }
1022
1023
1024 bool InsetTabular::moveDown(LCursor & cur)
1025 {
1026         if (tabular.row_of_cell(cur.idx()) == tabular.rows() - 1)
1027                 return false;
1028         cur.idx() = tabular.getCellBelow(cur.idx());
1029         resetPos(cur);
1030         return true;
1031 }
1032
1033
1034 bool InsetTabular::moveDownLock(LCursor & cur)
1035 {
1036         if (tabular.row_of_cell(cur.idx()) == tabular.rows() - 1)
1037                 return false;
1038         cur.idx() = tabular.getCellBelow(cur.idx());
1039         resetPos(cur);
1040         activateCellInset(cur, cur.idx(), cur.x_target());
1041         return true;
1042 }
1043
1044
1045 bool InsetTabular::moveNextCell(LCursor & cur)
1046 {
1047         lyxerr << "InsetTabular::moveNextCell 1 cur: " << cur << endl;
1048         if (isRightToLeft(cur)) {
1049                 if (tabular.isFirstCellInRow(cur.idx())) {
1050                         int row = tabular.row_of_cell(cur.idx());
1051                         if (row == tabular.rows() - 1)
1052                                 return false;
1053                         cur.idx() = tabular.getLastCellInRow(row);
1054                         cur.idx() = tabular.getCellBelow(cur.idx());
1055                 } else {
1056                         if (cur.idx() == 0)
1057                                 return false;
1058                         --cur.idx();
1059                 }
1060         } else {
1061                 if (tabular.isLastCell(cur.idx()))
1062                         return false;
1063                 ++cur.idx();
1064         }
1065         cur.par() = 0;
1066         cur.pos() = 0;
1067         lyxerr << "InsetTabular::moveNextCell 2 cur: " << cur << endl;
1068         resetPos(cur);
1069         return true;
1070 }
1071
1072
1073 bool InsetTabular::movePrevCell(LCursor & cur)
1074 {
1075         if (isRightToLeft(cur)) {
1076                 if (tabular.isLastCellInRow(cur.idx())) {
1077                         int row = tabular.row_of_cell(cur.idx());
1078                         if (row == 0)
1079                                 return false;
1080                         cur.idx() = tabular.getFirstCellInRow(row);
1081                         cur.idx() = tabular.getCellAbove(cur.idx());
1082                 } else {
1083                         if (tabular.isLastCell(cur.idx()))
1084                                 return false;
1085                         ++cur.idx();
1086                 }
1087         } else {
1088                 if (cur.idx() == 0) // first cell
1089                         return false;
1090                 --cur.idx();
1091         }
1092         cur.par() = 0;
1093         cur.pos() = 0;
1094         resetPos(cur);
1095         return true;
1096 }
1097
1098
1099 bool InsetTabular::tabularFeatures(LCursor & cur, string const & what)
1100 {
1101         LyXTabular::Feature action = LyXTabular::LAST_ACTION;
1102
1103         int i = 0;
1104         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
1105                 string const tmp = tabularFeature[i].feature;
1106
1107                 if (tmp == what.substr(0, tmp.length())) {
1108                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
1109                         //tabularFeatures[i].feature.length())) {
1110                         action = tabularFeature[i].action;
1111                         break;
1112                 }
1113         }
1114         if (action == LyXTabular::LAST_ACTION)
1115                 return false;
1116
1117         string const val =
1118                 ltrim(what.substr(tabularFeature[i].feature.length()));
1119         tabularFeatures(cur, action, val);
1120         return true;
1121 }
1122
1123
1124 namespace {
1125
1126 void checkLongtableSpecial(LyXTabular::ltType & ltt,
1127                           string const & special, bool & flag)
1128 {
1129         if (special == "dl_above") {
1130                 ltt.topDL = flag;
1131                 ltt.set = false;
1132         } else if (special == "dl_below") {
1133                 ltt.bottomDL = flag;
1134                 ltt.set = false;
1135         } else if (special == "empty") {
1136                 ltt.empty = flag;
1137                 ltt.set = false;
1138         } else if (flag) {
1139                 ltt.empty = false;
1140                 ltt.set = true;
1141         }
1142 }
1143
1144 } // anon namespace
1145
1146
1147 void InsetTabular::tabularFeatures(LCursor & cur,
1148         LyXTabular::Feature feature, string const & value)
1149 {
1150         BufferView & bv = cur.bv();
1151         int actcell = cur.idx();
1152         int sel_col_start;
1153         int sel_col_end;
1154         int sel_row_start;
1155         int sel_row_end;
1156         bool setLines = false;
1157         LyXAlignment setAlign = LYX_ALIGN_LEFT;
1158         LyXTabular::VAlignment setVAlign = LyXTabular::LYX_VALIGN_TOP;
1159
1160         switch (feature) {
1161
1162         case LyXTabular::M_ALIGN_LEFT:
1163         case LyXTabular::ALIGN_LEFT:
1164                 setAlign = LYX_ALIGN_LEFT;
1165                 break;
1166
1167         case LyXTabular::M_ALIGN_RIGHT:
1168         case LyXTabular::ALIGN_RIGHT:
1169                 setAlign = LYX_ALIGN_RIGHT;
1170                 break;
1171
1172         case LyXTabular::M_ALIGN_CENTER:
1173         case LyXTabular::ALIGN_CENTER:
1174                 setAlign = LYX_ALIGN_CENTER;
1175                 break;
1176
1177         case LyXTabular::ALIGN_BLOCK:
1178                 setAlign = LYX_ALIGN_BLOCK;
1179                 break;
1180
1181         case LyXTabular::M_VALIGN_TOP:
1182         case LyXTabular::VALIGN_TOP:
1183                 setVAlign = LyXTabular::LYX_VALIGN_TOP;
1184                 break;
1185
1186         case LyXTabular::M_VALIGN_BOTTOM:
1187         case LyXTabular::VALIGN_BOTTOM:
1188                 setVAlign = LyXTabular::LYX_VALIGN_BOTTOM;
1189                 break;
1190
1191         case LyXTabular::M_VALIGN_MIDDLE:
1192         case LyXTabular::VALIGN_MIDDLE:
1193                 setVAlign = LyXTabular::LYX_VALIGN_MIDDLE;
1194                 break;
1195
1196         default:
1197                 break;
1198         }
1199
1200         recordUndo(cur, Undo::ATOMIC);
1201
1202         getSelection(cur, sel_row_start, sel_row_end, sel_col_start, sel_col_end);
1203         int row =  tabular.row_of_cell(actcell);
1204         int column = tabular.column_of_cell(actcell);
1205         bool flag = true;
1206         LyXTabular::ltType ltt;
1207
1208         switch (feature) {
1209
1210         case LyXTabular::SET_PWIDTH: {
1211                 LyXLength const len(value);
1212                 tabular.setColumnPWidth(actcell, len);
1213                 if (len.zero()
1214                     && tabular.getAlignment(actcell, true) == LYX_ALIGN_BLOCK)
1215                         tabularFeatures(cur, LyXTabular::ALIGN_CENTER, string());
1216                 else if (!len.zero()
1217                          && tabular.getAlignment(actcell, true) != LYX_ALIGN_BLOCK)
1218                         tabularFeatures(cur, LyXTabular::ALIGN_BLOCK, string());
1219                 break;
1220         }
1221
1222         case LyXTabular::SET_MPWIDTH:
1223                 tabular.setMColumnPWidth(actcell, LyXLength(value));
1224                 break;
1225
1226         case LyXTabular::SET_SPECIAL_COLUMN:
1227         case LyXTabular::SET_SPECIAL_MULTI:
1228                 tabular.setAlignSpecial(actcell,value,feature);
1229                 break;
1230
1231         case LyXTabular::APPEND_ROW:
1232                 // append the row into the tabular
1233                 tabular.appendRow(bv.buffer()->params(), actcell);
1234                 tabular.setOwner(this);
1235                 break;
1236
1237         case LyXTabular::APPEND_COLUMN:
1238                 // append the column into the tabular
1239                 tabular.appendColumn(bv.buffer()->params(), actcell);
1240                 tabular.setOwner(this);
1241                 actcell = tabular.getCellNumber(row, column);
1242                 break;
1243
1244         case LyXTabular::DELETE_ROW:
1245                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1246                         tabular.deleteRow(sel_row_start);
1247                 if (sel_row_start >= tabular.rows())
1248                         --sel_row_start;
1249                 actcell = tabular.getCellNumber(sel_row_start, column);
1250                 cur.selection() = false;
1251                 break;
1252
1253         case LyXTabular::DELETE_COLUMN:
1254                 for (int i = sel_col_start; i <= sel_col_end; ++i)
1255                         tabular.deleteColumn(sel_col_start);
1256                 if (sel_col_start >= tabular.columns())
1257                         --sel_col_start;
1258                 actcell = tabular.getCellNumber(row, sel_col_start);
1259                 cur.selection() = false;
1260                 break;
1261
1262         case LyXTabular::M_TOGGLE_LINE_TOP:
1263                 flag = false;
1264         case LyXTabular::TOGGLE_LINE_TOP: {
1265                 bool lineSet = !tabular.topLine(actcell, flag);
1266                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1267                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1268                                 tabular.setTopLine(
1269                                         tabular.getCellNumber(i, j),
1270                                         lineSet, flag);
1271                 break;
1272         }
1273
1274         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
1275                 flag = false;
1276         case LyXTabular::TOGGLE_LINE_BOTTOM: {
1277                 bool lineSet = !tabular.bottomLine(actcell, flag);
1278                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1279                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1280                                 tabular.setBottomLine(
1281                                         tabular.getCellNumber(i, j),
1282                                         lineSet,
1283                                         flag);
1284                 break;
1285         }
1286
1287         case LyXTabular::M_TOGGLE_LINE_LEFT:
1288                 flag = false;
1289         case LyXTabular::TOGGLE_LINE_LEFT: {
1290                 bool lineSet = !tabular.leftLine(actcell, flag);
1291                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1292                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1293                                 tabular.setLeftLine(
1294                                         tabular.getCellNumber(i,j),
1295                                         lineSet,
1296                                         flag);
1297                 break;
1298         }
1299
1300         case LyXTabular::M_TOGGLE_LINE_RIGHT:
1301                 flag = false;
1302         case LyXTabular::TOGGLE_LINE_RIGHT: {
1303                 bool lineSet = !tabular.rightLine(actcell, flag);
1304                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1305                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1306                                 tabular.setRightLine(
1307                                         tabular.getCellNumber(i,j),
1308                                         lineSet,
1309                                         flag);
1310                 break;
1311         }
1312
1313         case LyXTabular::M_ALIGN_LEFT:
1314         case LyXTabular::M_ALIGN_RIGHT:
1315         case LyXTabular::M_ALIGN_CENTER:
1316                 flag = false;
1317         case LyXTabular::ALIGN_LEFT:
1318         case LyXTabular::ALIGN_RIGHT:
1319         case LyXTabular::ALIGN_CENTER:
1320         case LyXTabular::ALIGN_BLOCK:
1321                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1322                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1323                                 tabular.setAlignment(
1324                                         tabular.getCellNumber(i, j),
1325                                         setAlign,
1326                                         flag);
1327                 break;
1328
1329         case LyXTabular::M_VALIGN_TOP:
1330         case LyXTabular::M_VALIGN_BOTTOM:
1331         case LyXTabular::M_VALIGN_MIDDLE:
1332                 flag = false;
1333         case LyXTabular::VALIGN_TOP:
1334         case LyXTabular::VALIGN_BOTTOM:
1335         case LyXTabular::VALIGN_MIDDLE:
1336                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1337                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1338                                 tabular.setVAlignment(
1339                                         tabular.getCellNumber(i, j),
1340                                         setVAlign, flag);
1341                 break;
1342
1343         case LyXTabular::MULTICOLUMN: {
1344                 if (sel_row_start != sel_row_end) {
1345 #ifdef WITH_WARNINGS
1346 #warning Need I say it ? This is horrible.
1347 #endif
1348                         Alert::error(_("Error setting multicolumn"),
1349                                    _("You cannot set multicolumn vertically."));
1350                         return;
1351                 }
1352 #if 0
1353                 // just multicol for one Single Cell
1354                 if (!hasSelection()) {
1355                         // check wether we are completly in a multicol
1356                         if (tabular.isMultiColumn(actcell))
1357                                 tabular.unsetMultiColumn(actcell);
1358                         else
1359                                 tabular.setMultiColumn(bv.buffer(), actcell, 1);
1360                         break;
1361                 }
1362                 // we have a selection so this means we just add all this
1363                 // cells to form a multicolumn cell
1364                 int s_start;
1365                 int s_end;
1366
1367                 if (sel_cell_start > sel_cell_end) {
1368                         s_start = sel_cell_end;
1369                         s_end = sel_cell_start;
1370                 } else {
1371                         s_start = sel_cell_start;
1372                         s_end = sel_cell_end;
1373                 }
1374                 tabular.setMultiColumn(bv.buffer(), s_start, s_end - s_start + 1);
1375                 actcell = s_start;
1376 #endif
1377                 cur.selection() = false;
1378                 break;
1379         }
1380
1381         case LyXTabular::SET_ALL_LINES:
1382                 setLines = true;
1383         case LyXTabular::UNSET_ALL_LINES:
1384 #if 0
1385                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1386                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1387                                 tabular.setAllLines(
1388                                         tabular.getCellNumber(i,j), setLines);
1389 #endif
1390                 break;
1391
1392         case LyXTabular::SET_LONGTABULAR:
1393                 tabular.setLongTabular(true);
1394                 break;
1395
1396         case LyXTabular::UNSET_LONGTABULAR:
1397                 tabular.setLongTabular(false);
1398                 break;
1399
1400         case LyXTabular::SET_ROTATE_TABULAR:
1401                 tabular.setRotateTabular(true);
1402                 break;
1403
1404         case LyXTabular::UNSET_ROTATE_TABULAR:
1405                 tabular.setRotateTabular(false);
1406                 break;
1407
1408         case LyXTabular::SET_ROTATE_CELL:
1409                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1410                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1411                                 tabular.setRotateCell(
1412                                         tabular.getCellNumber(i, j), true);
1413                 break;
1414
1415         case LyXTabular::UNSET_ROTATE_CELL:
1416                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1417                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1418                                 tabular.setRotateCell(
1419                                         tabular.getCellNumber(i, j), false);
1420                 break;
1421
1422         case LyXTabular::SET_USEBOX: {
1423                 LyXTabular::BoxType val = LyXTabular::BoxType(strToInt(value));
1424                 if (val == tabular.getUsebox(actcell))
1425                         val = LyXTabular::BOX_NONE;
1426                 for (int i = sel_row_start; i <= sel_row_end; ++i)
1427                         for (int j = sel_col_start; j <= sel_col_end; ++j)
1428                                 tabular.setUsebox(tabular.getCellNumber(i, j), val);
1429                 break;
1430         }
1431
1432         case LyXTabular::UNSET_LTFIRSTHEAD:
1433                 flag = false;
1434         case LyXTabular::SET_LTFIRSTHEAD:
1435                 tabular.getRowOfLTFirstHead(row, ltt);
1436                 checkLongtableSpecial(ltt, value, flag);
1437                 tabular.setLTHead(row, flag, ltt, true);
1438                 break;
1439
1440         case LyXTabular::UNSET_LTHEAD:
1441                 flag = false;
1442         case LyXTabular::SET_LTHEAD:
1443                 tabular.getRowOfLTHead(row, ltt);
1444                 checkLongtableSpecial(ltt, value, flag);
1445                 tabular.setLTHead(row, flag, ltt, false);
1446                 break;
1447
1448         case LyXTabular::UNSET_LTFOOT:
1449                 flag = false;
1450         case LyXTabular::SET_LTFOOT:
1451                 tabular.getRowOfLTFoot(row, ltt);
1452                 checkLongtableSpecial(ltt, value, flag);
1453                 tabular.setLTFoot(row, flag, ltt, false);
1454                 break;
1455
1456         case LyXTabular::UNSET_LTLASTFOOT:
1457                 flag = false;
1458         case LyXTabular::SET_LTLASTFOOT:
1459                 tabular.getRowOfLTLastFoot(row, ltt);
1460                 checkLongtableSpecial(ltt, value, flag);
1461                 tabular.setLTFoot(row, flag, ltt, true);
1462                 break;
1463
1464         case LyXTabular::SET_LTNEWPAGE:
1465                 tabular.setLTNewPage(row, !tabular.getLTNewPage(row));
1466                 break;
1467
1468         // dummy stuff just to avoid warnings
1469         case LyXTabular::LAST_ACTION:
1470                 break;
1471         }
1472
1473         updateLocal(cur);
1474         InsetTabularMailer(*this).updateDialog(&bv);
1475 }
1476
1477
1478 void InsetTabular::activateCellInset(LCursor & cur, int cell, int x, int y)
1479 {
1480         cur.idx() = cell;
1481         tabular.getCellInset(cell).editXY(cur, x, y);
1482         updateLocal(cur);
1483 }
1484
1485
1486 void InsetTabular::activateCellInset(LCursor & cur, int cell, bool behind)
1487 {
1488         cur.idx() = cell;
1489         tabular.getCellInset(cell).edit(cur, behind);
1490         updateLocal(cur);
1491 }
1492
1493
1494 bool InsetTabular::showInsetDialog(BufferView * bv) const
1495 {
1496         InsetTabularMailer(*this).showDialog(bv);
1497         return true;
1498 }
1499
1500
1501 void InsetTabular::openLayoutDialog(BufferView * bv) const
1502 {
1503         InsetTabularMailer(*this).showDialog(bv);
1504 }
1505
1506
1507 //
1508 // function returns an object as defined in func_status.h:
1509 // states OK, Unknown, Disabled, On, Off.
1510 //
1511 FuncStatus InsetTabular::getStatus(BufferView & bv,
1512         string const & what, int actcell) const
1513 {
1514         FuncStatus status;
1515         int action = LyXTabular::LAST_ACTION;   
1516         LCursor & cur = bv.cursor();
1517
1518         int i = 0;
1519         for (; tabularFeature[i].action != LyXTabular::LAST_ACTION; ++i) {
1520                 string const tmp = tabularFeature[i].feature;
1521                 if (tmp == what.substr(0, tmp.length())) {
1522                         //if (!compare(tabularFeatures[i].feature.c_str(), what.c_str(),
1523                         //   tabularFeatures[i].feature.length()))
1524                         action = tabularFeature[i].action;
1525                         break;
1526                 }
1527         }
1528         if (action == LyXTabular::LAST_ACTION) {
1529                 status.clear();
1530                 status.unknown(true);
1531                 return status;
1532         }
1533
1534         string const argument
1535                 = ltrim(what.substr(tabularFeature[i].feature.length()));
1536
1537         int sel_row_start = 0;
1538         int sel_row_end = 0;
1539         int dummy;
1540         LyXTabular::ltType dummyltt;
1541         bool flag = true;
1542
1543         getSelection(cur, sel_row_start, sel_row_end, dummy, dummy);
1544
1545         switch (action) {
1546         case LyXTabular::SET_PWIDTH:
1547         case LyXTabular::SET_MPWIDTH:
1548         case LyXTabular::SET_SPECIAL_COLUMN:
1549         case LyXTabular::SET_SPECIAL_MULTI:
1550         case LyXTabular::APPEND_ROW:
1551         case LyXTabular::APPEND_COLUMN:
1552         case LyXTabular::DELETE_ROW:
1553         case LyXTabular::DELETE_COLUMN:
1554         case LyXTabular::SET_ALL_LINES:
1555         case LyXTabular::UNSET_ALL_LINES:
1556                 status.clear();
1557                 return status;
1558
1559         case LyXTabular::MULTICOLUMN:
1560                 status.setOnOff(tabular.isMultiColumn(actcell));
1561                 break;
1562
1563         case LyXTabular::M_TOGGLE_LINE_TOP:
1564                 flag = false;
1565         case LyXTabular::TOGGLE_LINE_TOP:
1566                 status.setOnOff(tabular.topLine(actcell, flag));
1567                 break;
1568
1569         case LyXTabular::M_TOGGLE_LINE_BOTTOM:
1570                 flag = false;
1571         case LyXTabular::TOGGLE_LINE_BOTTOM:
1572                 status.setOnOff(tabular.bottomLine(actcell, flag));
1573                 break;
1574
1575         case LyXTabular::M_TOGGLE_LINE_LEFT:
1576                 flag = false;
1577         case LyXTabular::TOGGLE_LINE_LEFT:
1578                 status.setOnOff(tabular.leftLine(actcell, flag));
1579                 break;
1580
1581         case LyXTabular::M_TOGGLE_LINE_RIGHT:
1582                 flag = false;
1583         case LyXTabular::TOGGLE_LINE_RIGHT:
1584                 status.setOnOff(tabular.rightLine(actcell, flag));
1585                 break;
1586
1587         case LyXTabular::M_ALIGN_LEFT:
1588                 flag = false;
1589         case LyXTabular::ALIGN_LEFT:
1590                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_LEFT);
1591                 break;
1592
1593         case LyXTabular::M_ALIGN_RIGHT:
1594                 flag = false;
1595         case LyXTabular::ALIGN_RIGHT:
1596                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_RIGHT);
1597                 break;
1598
1599         case LyXTabular::M_ALIGN_CENTER:
1600                 flag = false;
1601         case LyXTabular::ALIGN_CENTER:
1602                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_CENTER);
1603                 break;
1604
1605         case LyXTabular::ALIGN_BLOCK:
1606                 status.disabled(tabular.getPWidth(actcell).zero());
1607                 status.setOnOff(tabular.getAlignment(actcell, flag) == LYX_ALIGN_BLOCK);
1608                 break;
1609
1610         case LyXTabular::M_VALIGN_TOP:
1611                 flag = false;
1612         case LyXTabular::VALIGN_TOP:
1613                 status.setOnOff(
1614                         tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_TOP);
1615                 break;
1616
1617         case LyXTabular::M_VALIGN_BOTTOM:
1618                 flag = false;
1619         case LyXTabular::VALIGN_BOTTOM:
1620                 status.setOnOff(
1621                         tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_BOTTOM);
1622                 break;
1623
1624         case LyXTabular::M_VALIGN_MIDDLE:
1625                 flag = false;
1626         case LyXTabular::VALIGN_MIDDLE:
1627                 status.setOnOff(
1628                         tabular.getVAlignment(actcell, flag) == LyXTabular::LYX_VALIGN_MIDDLE);
1629                 break;
1630
1631         case LyXTabular::SET_LONGTABULAR:
1632                 status.setOnOff(tabular.isLongTabular());
1633                 break;
1634
1635         case LyXTabular::UNSET_LONGTABULAR:
1636                 status.setOnOff(!tabular.isLongTabular());
1637                 break;
1638
1639         case LyXTabular::SET_ROTATE_TABULAR:
1640                 status.setOnOff(tabular.getRotateTabular());
1641                 break;
1642
1643         case LyXTabular::UNSET_ROTATE_TABULAR:
1644                 status.setOnOff(!tabular.getRotateTabular());
1645                 break;
1646
1647         case LyXTabular::SET_ROTATE_CELL:
1648                 status.setOnOff(tabular.getRotateCell(actcell));
1649                 break;
1650
1651         case LyXTabular::UNSET_ROTATE_CELL:
1652                 status.setOnOff(!tabular.getRotateCell(actcell));
1653                 break;
1654
1655         case LyXTabular::SET_USEBOX:
1656                 status.setOnOff(strToInt(argument) == tabular.getUsebox(actcell));
1657                 break;
1658
1659         case LyXTabular::SET_LTFIRSTHEAD:
1660                 status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
1661                 break;
1662
1663         case LyXTabular::SET_LTHEAD:
1664                 status.setOnOff(tabular.getRowOfLTHead(sel_row_start, dummyltt));
1665                 break;
1666
1667         case LyXTabular::SET_LTFOOT:
1668                 status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
1669                 break;
1670
1671         case LyXTabular::SET_LTLASTFOOT:
1672                 status.setOnOff(tabular.getRowOfLTFoot(sel_row_start, dummyltt));
1673                 break;
1674
1675         case LyXTabular::SET_LTNEWPAGE:
1676                 status.setOnOff(tabular.getLTNewPage(sel_row_start));
1677                 break;
1678
1679         default:
1680                 status.clear();
1681                 status.disabled(true);
1682                 break;
1683         }
1684         return status;
1685 }
1686
1687
1688 void InsetTabular::getLabelList(Buffer const & buffer,
1689                                 vector<string> & list) const
1690 {
1691         tabular.getLabelList(buffer, list);
1692 }
1693
1694
1695 bool InsetTabular::copySelection(LCursor & cur)
1696 {
1697         if (!cur.selection())
1698                 return false;
1699
1700         int rs, re, cs, ce;
1701         getSelection(cur, rs, re, cs, ce);
1702
1703         paste_tabular.reset(new LyXTabular(tabular));
1704         paste_tabular->setOwner(this);
1705
1706         for (int i = 0; i < rs; ++i)
1707                 paste_tabular->deleteRow(0);
1708
1709         int const rows = re - rs + 1;
1710         while (paste_tabular->rows() > rows)
1711                 paste_tabular->deleteRow(rows);
1712
1713         paste_tabular->setTopLine(0, true, true);
1714         paste_tabular->setBottomLine(paste_tabular->getFirstCellInRow(rows - 1),
1715                                      true, true);
1716
1717         for (int i = 0; i < cs; ++i)
1718                 paste_tabular->deleteColumn(0);
1719
1720         int const columns = ce - cs + 1;
1721         while (paste_tabular->columns() > columns)
1722                 paste_tabular->deleteColumn(columns);
1723
1724         paste_tabular->setLeftLine(0, true, true);
1725         paste_tabular->setRightLine(paste_tabular->getLastCellInRow(0),
1726                                     true, true);
1727
1728         ostringstream os;
1729         OutputParams const runparams;
1730         paste_tabular->plaintext(*cur.bv().buffer(), os, runparams, 0, true, '\t');
1731         cur.bv().stuffClipboard(os.str());
1732         return true;
1733 }
1734
1735
1736 bool InsetTabular::pasteSelection(LCursor & cur)
1737 {
1738         if (!paste_tabular)
1739                 return false;
1740         int actcell = cur.idx();
1741         int actcol = tabular.column_of_cell(actcell);
1742         int actrow = tabular.row_of_cell(actcell);
1743         for (int r1 = 0, r2 = actrow;
1744              r1 < paste_tabular->rows() && r2 < tabular.rows();
1745              ++r1, ++r2) {
1746                 for (int c1 = 0, c2 = actcol;
1747                     c1 < paste_tabular->columns() && c2 < tabular.columns();
1748                     ++c1, ++c2) {
1749                         if (paste_tabular->isPartOfMultiColumn(r1, c1) &&
1750                             tabular.isPartOfMultiColumn(r2, c2))
1751                                 continue;
1752                         if (paste_tabular->isPartOfMultiColumn(r1, c1)) {
1753                                 --c2;
1754                                 continue;
1755                         }
1756                         if (tabular.isPartOfMultiColumn(r2, c2)) {
1757                                 --c1;
1758                                 continue;
1759                         }
1760                         InsetText & inset = tabular.getCellInset(r2, c2);
1761                         inset = paste_tabular->getCellInset(r1, c1);
1762                         inset.setOwner(this);
1763                         inset.markNew();
1764                 }
1765         }
1766         return true;
1767 }
1768
1769
1770 void InsetTabular::cutSelection(LCursor & cur)
1771 {
1772         if (!cur.selection())
1773                 return;
1774
1775         bool const track = cur.bv().buffer()->params().tracking_changes;
1776         int rs, re, cs, ce;
1777         getSelection(cur, rs, re, cs, ce); 
1778         for (int i = rs; i <= re; ++i)
1779                 for (int j = cs; j <= ce; ++j)
1780                         tabular.getCellInset(tabular.getCellNumber(i, j)).clear(track);
1781 }
1782
1783
1784 bool InsetTabular::isRightToLeft(LCursor & cur)
1785 {
1786         return cur.bv().getParentLanguage(this)->RightToLeft();
1787 }
1788
1789
1790 void InsetTabular::getSelection(LCursor & cur,
1791         int & rs, int & re, int & cs, int & ce) const
1792 {
1793         CursorSlice & beg = cur.selBegin();
1794         CursorSlice & end = cur.selEnd();
1795         cs = tabular.column_of_cell(beg.idx());
1796         ce = tabular.column_of_cell(end.idx());
1797         if (cs > ce) {
1798                 ce = cs;
1799                 cs = tabular.column_of_cell(end.idx());
1800         } else {
1801                 ce = tabular.right_column_of_cell(end.idx());
1802         }
1803
1804         rs = tabular.row_of_cell(beg.idx());
1805         re = tabular.row_of_cell(end.idx());
1806         if (rs > re)
1807                 swap(rs, re);
1808 }
1809
1810
1811 int InsetTabular::numParagraphs() const
1812 {
1813         return tabular.getNumberOfCells();
1814 }
1815
1816
1817 LyXText * InsetTabular::getText(int i) const
1818 {
1819         return i < tabular.getNumberOfCells()
1820                 ? tabular.getCellInset(i).getText(0)
1821                 : 0;
1822 }
1823
1824
1825 void InsetTabular::markErased()
1826 {
1827         for (int cell = 0; cell < tabular.getNumberOfCells(); ++cell)
1828                 tabular.getCellInset(cell).markErased();
1829 }
1830
1831
1832 bool InsetTabular::forceDefaultParagraphs(InsetBase const *) const
1833 {
1834 #if 0
1835         const int cell = tabular.getCellFromInset(in);
1836
1837         if (cell != -1)
1838                 return tabular.getPWidth(cell).zero();
1839
1840         // this is a workaround for a crash (New, Insert->Tabular,
1841         // Insert->FootNote)
1842         if (!owner())
1843                 return false;
1844
1845         // well we didn't obviously find it so maybe our owner knows more
1846         BOOST_ASSERT(owner());
1847         return owner()->forceDefaultParagraphs(in);
1848 #endif
1849         return false;
1850 }
1851
1852
1853 bool InsetTabular::insertAsciiString(BufferView & bv, string const & buf,
1854                                      bool usePaste)
1855 {
1856         if (buf.length() <= 0)
1857                 return true;
1858
1859         int cols = 1;
1860         int rows = 1;
1861         int maxCols = 1;
1862         string::size_type len = buf.length();
1863         string::size_type p = 0;
1864
1865         int actcell = bv.cursor().idx();
1866         int actcol = tabular.column_of_cell(actcell);
1867         int actrow = tabular.row_of_cell(actcell);
1868
1869         while (p < len && (p = buf.find_first_of("\t\n", p)) != string::npos) {
1870                 switch (buf[p]) {
1871                 case '\t':
1872                         ++cols;
1873                         break;
1874                 case '\n':
1875                         if (p + 1 < len)
1876                                 ++rows;
1877                         maxCols = max(cols, maxCols);
1878                         cols = 1;
1879                         break;
1880                 }
1881                 ++p;
1882         }
1883         maxCols = max(cols, maxCols);
1884         LyXTabular * loctab;
1885         int cell = 0;
1886         int ocol = 0;
1887         int row = 0;
1888         if (usePaste) {
1889                 paste_tabular.reset(
1890                         new LyXTabular(bv.buffer()->params(), rows, maxCols));
1891                 paste_tabular->setOwner(this);
1892                 loctab = paste_tabular.get();
1893                 cols = 0;
1894         } else {
1895                 loctab = &tabular;
1896                 cell = actcell;
1897                 ocol = actcol;
1898                 row = actrow;
1899         }
1900
1901         string::size_type op = 0;
1902         int cells = loctab->getNumberOfCells();
1903         p = 0;
1904         cols = ocol;
1905         rows = loctab->rows();
1906         int const columns = loctab->columns();
1907
1908         while (cell < cells && p < len && row < rows &&
1909                (p = buf.find_first_of("\t\n", p)) != string::npos)
1910         {
1911                 if (p >= len)
1912                         break;
1913                 switch (buf[p]) {
1914                 case '\t':
1915                         // we can only set this if we are not too far right
1916                         if (cols < columns) {
1917                                 InsetText & inset = loctab->getCellInset(cell);
1918                                 LyXFont const font = inset.text_.
1919                                         getFont(inset.paragraphs().begin(), 0);
1920                                 inset.setText(buf.substr(op, p - op), font);
1921                                 ++cols;
1922                                 ++cell;
1923                         }
1924                         break;
1925                 case '\n':
1926                         // we can only set this if we are not too far right
1927                         if (cols < columns) {
1928                                 InsetText & inset = tabular.getCellInset(cell);
1929                                 LyXFont const font = inset.text_.
1930                                         getFont(inset.paragraphs().begin(), 0);
1931                                 inset.setText(buf.substr(op, p - op), font);
1932                         }
1933                         cols = ocol;
1934                         ++row;
1935                         if (row < rows)
1936                                 cell = loctab->getCellNumber(row, cols);
1937                         break;
1938                 }
1939                 ++p;
1940                 op = p;
1941         }
1942         // check for the last cell if there is no trailing '\n'
1943         if (cell < cells && op < len) {
1944                 InsetText & inset = loctab->getCellInset(cell);
1945                 LyXFont const font = inset.text_.getFont(inset.paragraphs().begin(), 0);
1946                 inset.setText(buf.substr(op, len - op), font);
1947         }
1948         return true;
1949 }
1950
1951
1952 void InsetTabular::addPreview(PreviewLoader & loader) const
1953 {
1954         int const rows = tabular.rows();
1955         int const columns = tabular.columns();
1956         for (int i = 0; i < rows; ++i) {
1957                 for (int j = 0; j < columns; ++j)
1958                         tabular.getCellInset(i, j).addPreview(loader);
1959         }
1960 }
1961
1962
1963 bool InsetTabular::tablemode(LCursor & cur) const
1964 {
1965         return cur.selBegin().idx() != cur.selEnd().idx();
1966 }
1967
1968
1969 string const InsetTabularMailer::name_("tabular");
1970
1971 InsetTabularMailer::InsetTabularMailer(InsetTabular const & inset)
1972         : inset_(const_cast<InsetTabular &>(inset))
1973 {}
1974
1975
1976 string const InsetTabularMailer::inset2string(Buffer const &) const
1977 {
1978         return params2string(inset_);
1979 }
1980
1981
1982 int InsetTabularMailer::string2params(string const & in, InsetTabular & inset)
1983 {
1984         istringstream data(in);
1985         LyXLex lex(0,0);
1986         lex.setStream(data);
1987
1988 #warning CHECK verify that this is a sane value to return.
1989         if (in.empty())
1990                 return -1;
1991
1992         if (lex.isOK()) {
1993                 lex.next();
1994                 string const token = lex.getString();
1995                 if (token != name_)
1996                         return -1;
1997         }
1998
1999         int cell = -1;
2000         if (lex.isOK()) {
2001                 lex.next();
2002                 string const token = lex.getString();
2003                 if (token != "\\active_cell")
2004                         return -1;
2005                 lex.next();
2006                 cell = lex.getInteger();
2007         }
2008
2009         // This is part of the inset proper that is usually swallowed
2010         // by Buffer::readInset
2011         if (lex.isOK()) {
2012                 lex.next();
2013                 string const token = lex.getString();
2014                 if (token != "Tabular")
2015                         return -1;
2016         }
2017
2018         if (!lex.isOK())
2019                 return -1;
2020
2021         Buffer const & buffer = inset.buffer();
2022         inset.read(buffer, lex);
2023
2024         // We can't set the active cell, but we can tell the frontend
2025         // what it is.
2026         return cell;
2027 }
2028
2029
2030 string const InsetTabularMailer::params2string(InsetTabular const & inset)
2031 {
2032         ostringstream data;
2033 #warning wrong!
2034         //data << name_ << " \\active_cell " << inset.getActCell() << '\n';
2035         data << name_ << " \\active_cell " << 0 << '\n';
2036         inset.write(inset.buffer(), data);
2037         data << "\\end_inset\n";
2038         return data.str();
2039 }