]> git.lyx.org Git - lyx.git/blob - src/mathed/InsetMathGrid.cpp
Fix bug 5802 (http://bugzilla.lyx.org/show_bug.cgi?id=5802)
[lyx.git] / src / mathed / InsetMathGrid.cpp
1 /**
2  * \file InsetMathGrid.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author André Pönitz
7  *
8  * Full author contact details are available in file CREDITS.
9  */
10
11 #include <config.h>
12 #include <algorithm>
13
14 #include "InsetMathGrid.h"
15
16 #include "MathData.h"
17 #include "MathParser.h"
18 #include "MathStream.h"
19 #include "MetricsInfo.h"
20
21 #include "BufferView.h"
22 #include "CutAndPaste.h"
23 #include "FuncStatus.h"
24 #include "Cursor.h"
25 #include "FuncRequest.h"
26
27 #include "frontends/Clipboard.h"
28 #include "frontends/FontMetrics.h"
29 #include "frontends/Painter.h"
30
31 #include "support/debug.h"
32 #include "support/docstream.h"
33 #include "support/gettext.h"
34 #include "support/lstrings.h"
35
36 #include "support/lassert.h"
37
38 #include <sstream>
39
40 using namespace std;
41 using namespace lyx::support;
42
43
44 namespace lyx {
45
46 static docstring verboseHLine(int n)
47 {
48         docstring res;
49         for (int i = 0; i < n; ++i)
50                 res += "\\hline";
51         if (n)
52                 res += ' ';
53         return res;
54 }
55
56
57 static int extractInt(istream & is)
58 {
59         int num = 1;
60         is >> num;
61         return (num == 0) ? 1 : num;
62 }
63
64
65 static void resetGrid(InsetMathGrid & grid)
66 {
67         while (grid.ncols() > 1)
68                 grid.delCol(grid.ncols());
69         while (grid.nrows() > 1)
70                 grid.delRow(grid.nrows());
71         grid.cell(0).erase(0, grid.cell(0).size());
72         grid.setDefaults();
73 }
74
75
76
77 //////////////////////////////////////////////////////////////
78
79
80 InsetMathGrid::CellInfo::CellInfo()
81         : dummy_(false)
82 {}
83
84
85
86 //////////////////////////////////////////////////////////////
87
88
89 InsetMathGrid::RowInfo::RowInfo()
90         : lines_(0), skip_(0), allow_newpage_(true)
91 {}
92
93
94
95 int InsetMathGrid::RowInfo::skipPixels(MetricsInfo const & mi) const
96 {
97         frontend::FontMetrics const & fm = theFontMetrics(mi.base.font);
98         return crskip_.inPixels(mi.base.textwidth,
99                                 fm.width(char_type('M')));
100 }
101
102
103
104 //////////////////////////////////////////////////////////////
105
106
107 InsetMathGrid::ColInfo::ColInfo()
108         : align_('c'), lines_(0)
109 {}
110
111
112 //////////////////////////////////////////////////////////////
113
114
115 InsetMathGrid::InsetMathGrid()
116         : InsetMathNest(1),
117           rowinfo_(1 + 1),
118                 colinfo_(1 + 1),
119                 cellinfo_(1),
120                 v_align_('c')
121 {
122         setDefaults();
123 }
124
125
126 InsetMathGrid::InsetMathGrid(col_type m, row_type n)
127         : InsetMathNest(m * n),
128           rowinfo_(n + 1),
129                 colinfo_(m + 1),
130                 cellinfo_(m * n),
131                 v_align_('c')
132 {
133         setDefaults();
134 }
135
136
137 InsetMathGrid::InsetMathGrid(col_type m, row_type n, char v, docstring const & h)
138         : InsetMathNest(m * n),
139           rowinfo_(n + 1),
140           colinfo_(m + 1),
141                 cellinfo_(m * n),
142                 v_align_(v)
143 {
144         setDefaults();
145         setVerticalAlignment(v);
146         setHorizontalAlignments(h);
147 }
148
149
150 Inset * InsetMathGrid::clone() const
151 {
152         return new InsetMathGrid(*this);
153 }
154
155
156 InsetMath::idx_type InsetMathGrid::index(row_type row, col_type col) const
157 {
158         return col + ncols() * row;
159 }
160
161
162 void InsetMathGrid::setDefaults()
163 {
164         if (ncols() <= 0)
165                 lyxerr << "positive number of columns expected" << endl;
166         //if (nrows() <= 0)
167         //      lyxerr << "positive number of rows expected" << endl;
168         for (col_type col = 0; col < ncols(); ++col) {
169                 colinfo_[col].align_ = defaultColAlign(col);
170                 colinfo_[col].skip_  = defaultColSpace(col);
171                 colinfo_[col].special_.clear();
172         }
173 }
174
175
176 void InsetMathGrid::setHorizontalAlignments(docstring const & hh)
177 {
178         col_type col = 0;
179         for (docstring::const_iterator it = hh.begin(); it != hh.end(); ++it) {
180                 char_type c = *it;
181                 if (c == '|') {
182                         colinfo_[col].lines_++;
183                 } else if ((c == 'p' || c == 'm' || c == 'b'||
184                             c == '!' || c == '@' || c == '>' || c == '<') &&
185                            it + 1 != hh.end() && *(it + 1) == '{') {
186                         // @{decl.} and p{width} are standard LaTeX, the
187                         // others are extensions by array.sty
188                         bool const newcolumn = c == 'p' || c == 'm' || c == 'b';
189                         if (newcolumn) {
190                                 // this declares a new column
191                                 if (col >= ncols())
192                                         // Only intercolumn stuff is allowed
193                                         // in the last dummy column
194                                         break;
195                                 colinfo_[col].align_ = 'l';
196                         } else {
197                                 // this is intercolumn stuff
198                                 if (colinfo_[col].special_.empty())
199                                         // Overtake possible lines
200                                         colinfo_[col].special_ = docstring(colinfo_[col].lines_, '|');
201                         }
202                         int brace_open = 0;
203                         int brace_close = 0;
204                         while (it != hh.end()) {
205                                 c = *it;
206                                 colinfo_[col].special_ += c;
207                                 if (c == '{')
208                                         ++brace_open;
209                                 else if (c == '}')
210                                         ++brace_close;
211                                 ++it;
212                                 if (brace_open > 0 && brace_open == brace_close)
213                                         break;
214                         }
215                         --it;
216                         if (newcolumn) {
217                                 colinfo_[col].lines_ = count(
218                                         colinfo_[col].special_.begin(),
219                                         colinfo_[col].special_.end(), '|');
220                                 LYXERR(Debug::MATHED, "special column separator: `"
221                                         << to_utf8(colinfo_[col].special_) << '\'');
222                                 ++col;
223                                 colinfo_[col].lines_ = 0;
224                                 colinfo_[col].special_.clear();
225                         }
226                 } else if (col >= ncols()) {
227                         // Only intercolumn stuff is allowed in the last
228                         // dummy column
229                         break;
230                 } else if (c == 'c' || c == 'l' || c == 'r') {
231                         colinfo_[col].align_ = static_cast<char>(c);
232                         if (!colinfo_[col].special_.empty()) {
233                                 colinfo_[col].special_ += c;
234                                 colinfo_[col].lines_ = count(
235                                                 colinfo_[col].special_.begin(),
236                                                 colinfo_[col].special_.end(), '|');
237                                 LYXERR(Debug::MATHED, "special column separator: `"
238                                         << to_utf8(colinfo_[col].special_) << '\'');
239                         }
240                         ++col;
241                         colinfo_[col].lines_ = 0;
242                         colinfo_[col].special_.clear();
243                 } else {
244                         lyxerr << "unknown column separator: '" << c << "'" << endl;
245                 }
246         }
247
248 /*
249         col_type n = hh.size();
250         if (n > ncols())
251                 n = ncols();
252         for (col_type col = 0; col < n; ++col)
253                 colinfo_[col].align_ = hh[col];
254 */
255 }
256
257
258 InsetMathGrid::col_type InsetMathGrid::guessColumns(docstring const & hh)
259 {
260         col_type col = 0;
261         for (docstring::const_iterator it = hh.begin(); it != hh.end(); ++it)
262                 if (*it == 'c' || *it == 'l' || *it == 'r'||
263                     *it == 'p' || *it == 'm' || *it == 'b')
264                         ++col;
265         // let's have at least one column, even if we did not recognize its
266         // alignment
267         if (col == 0)
268                 col = 1;
269         return col;
270 }
271
272
273 void InsetMathGrid::setHorizontalAlignment(char h, col_type col)
274 {
275         colinfo_[col].align_ = h;
276         if (!colinfo_[col].special_.empty()) {
277                 char_type & c = colinfo_[col].special_[colinfo_[col].special_.size() - 1];
278                 if (c == 'l' || c == 'c' || c == 'r')
279                         c = h;
280         }
281         // FIXME: Change alignment of p, m and b columns, too
282 }
283
284
285 char InsetMathGrid::horizontalAlignment(col_type col) const
286 {
287         return colinfo_[col].align_;
288 }
289
290
291 docstring InsetMathGrid::horizontalAlignments() const
292 {
293         docstring res;
294         for (col_type col = 0; col < ncols(); ++col) {
295                 if (colinfo_[col].special_.empty()) {
296                         res += docstring(colinfo_[col].lines_, '|');
297                         res += colinfo_[col].align_;
298                 } else
299                         res += colinfo_[col].special_;
300         }
301         if (colinfo_[ncols()].special_.empty())
302                 return res + docstring(colinfo_[ncols()].lines_, '|');
303         return res + colinfo_[ncols()].special_;
304 }
305
306
307 void InsetMathGrid::setVerticalAlignment(char c)
308 {
309         v_align_ = c;
310 }
311
312
313 char InsetMathGrid::verticalAlignment() const
314 {
315         return v_align_;
316 }
317
318
319 InsetMathGrid::col_type InsetMathGrid::ncols() const
320 {
321         return colinfo_.size() - 1;
322 }
323
324
325 InsetMathGrid::row_type InsetMathGrid::nrows() const
326 {
327         return rowinfo_.size() - 1;
328 }
329
330
331 InsetMathGrid::col_type InsetMathGrid::col(idx_type idx) const
332 {
333         return idx % ncols();
334 }
335
336
337 InsetMathGrid::row_type InsetMathGrid::row(idx_type idx) const
338 {
339         return idx / ncols();
340 }
341
342
343 void InsetMathGrid::vcrskip(Length const & crskip, row_type row)
344 {
345         rowinfo_[row].crskip_ = crskip;
346 }
347
348
349 Length InsetMathGrid::vcrskip(row_type row) const
350 {
351         return rowinfo_[row].crskip_;
352 }
353
354
355 void InsetMathGrid::metrics(MetricsInfo & mi, Dimension & dim) const
356 {
357         // let the cells adjust themselves
358         InsetMathNest::metrics(mi);
359
360         BufferView & bv = *mi.base.bv;
361
362         // compute absolute sizes of vertical structure
363         for (row_type row = 0; row < nrows(); ++row) {
364                 int asc  = 0;
365                 int desc = 0;
366                 for (col_type col = 0; col < ncols(); ++col) {
367                         Dimension const & dimc = cell(index(row, col)).dimension(bv);
368                         asc  = max(asc,  dimc.asc);
369                         desc = max(desc, dimc.des);
370                 }
371                 rowinfo_[row].ascent_  = asc;
372                 rowinfo_[row].descent_ = desc;
373         }
374         rowinfo_[0].ascent_       += hlinesep() * rowinfo_[0].lines_;
375         rowinfo_[nrows()].ascent_  = 0;
376         rowinfo_[nrows()].descent_ = 0;
377
378         // compute vertical offsets
379         rowinfo_[0].offset_ = 0;
380         for (row_type row = 1; row <= nrows(); ++row) {
381                 rowinfo_[row].offset_  =
382                         rowinfo_[row - 1].offset_  +
383                         rowinfo_[row - 1].descent_ +
384                         rowinfo_[row - 1].skipPixels(mi) +
385                         rowsep() +
386                         rowinfo_[row].lines_ * hlinesep() +
387                         rowinfo_[row].ascent_;
388         }
389
390         // adjust vertical offset
391         int h = 0;
392         switch (v_align_) {
393                 case 't':
394                         h = 0;
395                         break;
396                 case 'b':
397                         h = rowinfo_[nrows() - 1].offset_;
398                         break;
399                 default:
400                         h = rowinfo_[nrows() - 1].offset_ / 2;
401         }
402         for (row_type row = 0; row <= nrows(); ++row)
403                 rowinfo_[row].offset_ -= h;
404
405
406         // compute absolute sizes of horizontal structure
407         for (col_type col = 0; col < ncols(); ++col) {
408                 int wid = 0;
409                 for (row_type row = 0; row < nrows(); ++row)
410                         wid = max(wid, cell(index(row, col)).dimension(bv).wid);
411                 colinfo_[col].width_ = wid;
412         }
413         colinfo_[ncols()].width_  = 0;
414
415         // compute horizontal offsets
416         colinfo_[0].offset_ = border();
417         for (col_type col = 1; col <= ncols(); ++col) {
418                 colinfo_[col].offset_ =
419                         colinfo_[col - 1].offset_ +
420                         colinfo_[col - 1].width_ +
421                         colinfo_[col - 1].skip_ +
422                         colsep() +
423                         colinfo_[col].lines_ * vlinesep();
424         }
425
426
427         dim.wid   =   colinfo_[ncols() - 1].offset_
428                        + colinfo_[ncols() - 1].width_
429                  + vlinesep() * colinfo_[ncols()].lines_
430                        + border();
431
432         dim.asc  = - rowinfo_[0].offset_
433                        + rowinfo_[0].ascent_
434                  + hlinesep() * rowinfo_[0].lines_
435                        + border();
436
437         dim.des =   rowinfo_[nrows() - 1].offset_
438                        + rowinfo_[nrows() - 1].descent_
439                  + hlinesep() * rowinfo_[nrows()].lines_
440                        + border();
441
442
443 /*
444         // Increase ws_[i] for 'R' columns (except the first one)
445         for (int i = 1; i < nc_; ++i)
446                 if (align_[i] == 'R')
447                         ws_[i] += 10 * df_width;
448         // Increase ws_[i] for 'C' column
449         if (align_[0] == 'C')
450                 if (ws_[0] < 7 * workwidth / 8)
451                         ws_[0] = 7 * workwidth / 8;
452
453         // Adjust local tabs
454         width = colsep();
455         for (cxrow = row_.begin(); cxrow; ++cxrow) {
456                 int rg = COLSEP;
457                 int lf = 0;
458                 for (int i = 0; i < nc_; ++i) {
459                         bool isvoid = false;
460                         if (cxrow->getTab(i) <= 0) {
461                                 cxrow->setTab(i, df_width);
462                                 isvoid = true;
463                         }
464                         switch (align_[i]) {
465                         case 'l':
466                                 lf = 0;
467                                 break;
468                         case 'c':
469                                 lf = (ws_[i] - cxrow->getTab(i))/2;
470                                 break;
471                         case 'r':
472                         case 'R':
473                                 lf = ws_[i] - cxrow->getTab(i);
474                                 break;
475                         case 'C':
476                                 if (cxrow == row_.begin())
477                                         lf = 0;
478                                 else if (cxrow.is_last())
479                                         lf = ws_[i] - cxrow->getTab(i);
480                                 else
481                                         lf = (ws_[i] - cxrow->getTab(i))/2;
482                                 break;
483                         }
484                         int const ww = (isvoid) ? lf : lf + cxrow->getTab(i);
485                         cxrow->setTab(i, lf + rg);
486                         rg = ws_[i] - ww + colsep();
487                         if (cxrow == row_.begin())
488                                 width += ws_[i] + colsep();
489                 }
490                 cxrow->setBaseline(cxrow->getBaseline() - ascent);
491         }
492 */
493         metricsMarkers2(dim);
494         // Cache the inset dimension.
495         setDimCache(mi, dim);
496 }
497
498
499 void InsetMathGrid::draw(PainterInfo & pi, int x, int y) const
500 {
501         drawWithMargin(pi, x, y, 1, 1);
502 }
503
504
505 void InsetMathGrid::drawWithMargin(PainterInfo & pi, int x, int y,
506         int lmargin, int rmargin) const
507 {
508         Dimension const dim = dimension(*pi.base.bv);
509         BufferView const & bv = *pi.base.bv;
510
511         for (idx_type idx = 0; idx < nargs(); ++idx)
512                 cell(idx).draw(pi, x + lmargin + cellXOffset(bv, idx),
513                         y + cellYOffset(idx));
514
515         for (row_type row = 0; row <= nrows(); ++row)
516                 for (unsigned int i = 0; i < rowinfo_[row].lines_; ++i) {
517                         int yy = y + rowinfo_[row].offset_ - rowinfo_[row].ascent_
518                                 - i * hlinesep() - hlinesep()/2 - rowsep()/2;
519                         pi.pain.line(x + lmargin + 1, yy,
520                                      x + dim.width() - rmargin - 1, yy,
521                                      Color_foreground);
522                 }
523
524         for (col_type col = 0; col <= ncols(); ++col)
525                 for (unsigned int i = 0; i < colinfo_[col].lines_; ++i) {
526                         int xx = x + lmargin + colinfo_[col].offset_
527                                 - i * vlinesep() - vlinesep()/2 - colsep()/2;
528                         pi.pain.line(xx, y - dim.ascent() + 1,
529                                      xx, y + dim.descent() - 1,
530                                      Color_foreground);
531                 }
532         drawMarkers2(pi, x, y);
533 }
534
535
536 void InsetMathGrid::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
537 {
538         // let the cells adjust themselves
539         //InsetMathNest::metrics(mi);
540         for (idx_type i = 0; i < nargs(); ++i)
541                 cell(i).metricsT(mi, dim);
542
543         // compute absolute sizes of vertical structure
544         for (row_type row = 0; row < nrows(); ++row) {
545                 int asc  = 0;
546                 int desc = 0;
547                 for (col_type col = 0; col < ncols(); ++col) {
548                         //MathData const & c = cell(index(row, col));
549                         // FIXME: BROKEN!
550                         Dimension dimc;
551                         asc  = max(asc,  dimc.ascent());
552                         desc = max(desc, dimc.descent());
553                 }
554                 rowinfo_[row].ascent_  = asc;
555                 rowinfo_[row].descent_ = desc;
556         }
557         //rowinfo_[0].ascent_       += hlinesep() * rowinfo_[0].lines_;
558         rowinfo_[nrows()].ascent_  = 0;
559         rowinfo_[nrows()].descent_ = 0;
560
561         // compute vertical offsets
562         rowinfo_[0].offset_ = 0;
563         for (row_type row = 1; row <= nrows(); ++row) {
564                 rowinfo_[row].offset_  =
565                         rowinfo_[row - 1].offset_  +
566                         rowinfo_[row - 1].descent_ +
567                         //rowinfo_[row - 1].skipPixels(mi) +
568                         1 + //rowsep() +
569                         //rowinfo_[row].lines_ * hlinesep() +
570                         rowinfo_[row].ascent_;
571         }
572
573         // adjust vertical offset
574         int h = 0;
575         switch (v_align_) {
576                 case 't':
577                         h = 0;
578                         break;
579                 case 'b':
580                         h = rowinfo_[nrows() - 1].offset_;
581                         break;
582                 default:
583                         h = rowinfo_[nrows() - 1].offset_ / 2;
584         }
585         for (row_type row = 0; row <= nrows(); ++row)
586                 rowinfo_[row].offset_ -= h;
587
588
589         // compute absolute sizes of horizontal structure
590         for (col_type col = 0; col < ncols(); ++col) {
591                 int wid = 0;
592                 for (row_type row = 0; row < nrows(); ++row) {
593                         // FIXME: BROKEN!
594                         //wid = max(wid, cell(index(row, col)).width());
595                 }
596                 colinfo_[col].width_ = wid;
597         }
598         colinfo_[ncols()].width_  = 0;
599
600         // compute horizontal offsets
601         colinfo_[0].offset_ = border();
602         for (col_type col = 1; col <= ncols(); ++col) {
603                 colinfo_[col].offset_ =
604                         colinfo_[col - 1].offset_ +
605                         colinfo_[col - 1].width_ +
606                         colinfo_[col - 1].skip_ +
607                         1 ; //colsep() +
608                         //colinfo_[col].lines_ * vlinesep();
609         }
610
611
612         dim.wid  =  colinfo_[ncols() - 1].offset_
613                        + colinfo_[ncols() - 1].width_
614                  //+ vlinesep() * colinfo_[ncols()].lines_
615                        + 2;
616
617         dim.asc  = -rowinfo_[0].offset_
618                        + rowinfo_[0].ascent_
619                  //+ hlinesep() * rowinfo_[0].lines_
620                        + 1;
621
622         dim.des  =  rowinfo_[nrows() - 1].offset_
623                        + rowinfo_[nrows() - 1].descent_
624                  //+ hlinesep() * rowinfo_[nrows()].lines_
625                        + 1;
626 }
627
628
629 void InsetMathGrid::drawT(TextPainter & /*pain*/, int /*x*/, int /*y*/) const
630 {
631 //      for (idx_type idx = 0; idx < nargs(); ++idx)
632 //              cell(idx).drawT(pain, x + cellXOffset(idx), y + cellYOffset(idx));
633 }
634
635
636 docstring InsetMathGrid::eolString(row_type row, bool emptyline, bool fragile) const
637 {
638         docstring eol;
639
640         if (!rowinfo_[row].crskip_.zero())
641                 eol += '[' + from_utf8(rowinfo_[row].crskip_.asLatexString()) + ']';
642         else if(!rowinfo_[row].allow_newpage_)
643                 eol += '*';
644
645         // make sure an upcoming '[' does not break anything
646         if (row + 1 < nrows()) {
647                 MathData const & c = cell(index(row + 1, 0));
648                 if (c.size() && c.front()->getChar() == '[')
649                         //eol += "[0pt]";
650                         eol += "{}";
651         }
652
653         // only add \\ if necessary
654         if (eol.empty() && row + 1 == nrows() && (nrows() == 1 || !emptyline))
655                 return docstring();
656
657         return (fragile ? "\\protect\\\\" : "\\\\") + eol;
658 }
659
660
661 docstring InsetMathGrid::eocString(col_type col, col_type lastcol) const
662 {
663         if (col + 1 == lastcol)
664                 return docstring();
665         return from_ascii(" & ");
666 }
667
668
669 void InsetMathGrid::addRow(row_type row)
670 {
671         rowinfo_.insert(rowinfo_.begin() + row + 1, RowInfo());
672         cells_.insert
673                 (cells_.begin() + (row + 1) * ncols(), ncols(), MathData());
674         cellinfo_.insert
675                 (cellinfo_.begin() + (row + 1) * ncols(), ncols(), CellInfo());
676 }
677
678
679 void InsetMathGrid::appendRow()
680 {
681         rowinfo_.push_back(RowInfo());
682         //cells_.insert(cells_.end(), ncols(), MathData());
683         for (col_type col = 0; col < ncols(); ++col) {
684                 cells_.push_back(cells_type::value_type());
685                 cellinfo_.push_back(CellInfo());
686         }
687 }
688
689
690 void InsetMathGrid::delRow(row_type row)
691 {
692         if (nrows() == 1)
693                 return;
694
695         cells_type::iterator it = cells_.begin() + row * ncols();
696         cells_.erase(it, it + ncols());
697
698         vector<CellInfo>::iterator jt = cellinfo_.begin() + row * ncols();
699         cellinfo_.erase(jt, jt + ncols());
700
701         rowinfo_.erase(rowinfo_.begin() + row);
702 }
703
704
705 void InsetMathGrid::copyRow(row_type row)
706 {
707         addRow(row);
708         for (col_type col = 0; col < ncols(); ++col)
709                 cells_[(row + 1) * ncols() + col] = cells_[row * ncols() + col];
710 }
711
712
713 void InsetMathGrid::swapRow(row_type row)
714 {
715         if (nrows() == 1)
716                 return;
717         if (row + 1 == nrows())
718                 --row;
719         for (col_type col = 0; col < ncols(); ++col)
720                 swap(cells_[row * ncols() + col], cells_[(row + 1) * ncols() + col]);
721 }
722
723
724 void InsetMathGrid::addCol(col_type newcol)
725 {
726         const col_type nc = ncols();
727         const row_type nr = nrows();
728         cells_type new_cells((nc + 1) * nr);
729         vector<CellInfo> new_cellinfo((nc + 1) * nr);
730
731         for (row_type row = 0; row < nr; ++row)
732                 for (col_type col = 0; col < nc; ++col) {
733                         new_cells[row * (nc + 1) + col + (col >= newcol)]
734                                 = cells_[row * nc + col];
735                         new_cellinfo[row * (nc + 1) + col + (col >= newcol)]
736                                 = cellinfo_[row * nc + col];
737                 }
738         swap(cells_, new_cells);
739         swap(cellinfo_, new_cellinfo);
740
741         ColInfo inf;
742         inf.skip_  = defaultColSpace(newcol);
743         inf.align_ = defaultColAlign(newcol);
744         colinfo_.insert(colinfo_.begin() + newcol, inf);
745 }
746
747
748 void InsetMathGrid::delCol(col_type col)
749 {
750         if (ncols() == 1)
751                 return;
752
753         cells_type tmpcells;
754         vector<CellInfo> tmpcellinfo;
755         for (col_type i = 0; i < nargs(); ++i)
756                 if (i % ncols() != col) {
757                         tmpcells.push_back(cells_[i]);
758                         tmpcellinfo.push_back(cellinfo_[i]);
759                 }
760         swap(cells_, tmpcells);
761         swap(cellinfo_, tmpcellinfo);
762
763         colinfo_.erase(colinfo_.begin() + col);
764 }
765
766
767 void InsetMathGrid::copyCol(col_type col)
768 {
769         addCol(col+1);
770         for (row_type row = 0; row < nrows(); ++row)
771                 cells_[row * ncols() + col + 1] = cells_[row * ncols() + col];
772 }
773
774
775 void InsetMathGrid::swapCol(col_type col)
776 {
777         if (ncols() == 1)
778                 return;
779         if (col + 1 == ncols())
780                 --col;
781         for (row_type row = 0; row < nrows(); ++row)
782                 swap(cells_[row * ncols() + col], cells_[row * ncols() + col + 1]);
783 }
784
785
786 int InsetMathGrid::cellXOffset(BufferView const & bv, idx_type idx) const
787 {
788         col_type c = col(idx);
789         int x = colinfo_[c].offset_;
790         char align = colinfo_[c].align_;
791         Dimension const & celldim = cell(idx).dimension(bv);
792         if (align == 'r' || align == 'R')
793                 x += colinfo_[c].width_ - celldim.wid;
794         if (align == 'c' || align == 'C')
795                 x += (colinfo_[c].width_ - celldim.wid) / 2;
796         return x;
797 }
798
799
800 int InsetMathGrid::cellYOffset(idx_type idx) const
801 {
802         return rowinfo_[row(idx)].offset_;
803 }
804
805
806 bool InsetMathGrid::idxUpDown(Cursor & cur, bool up) const
807 {
808         if (up) {
809                 if (cur.row() == 0)
810                         return false;
811                 cur.idx() -= ncols();
812         } else {
813                 if (cur.row() + 1 >= nrows())
814                         return false;
815                 cur.idx() += ncols();
816         }
817         cur.pos() = cur.cell().x2pos(&cur.bv(), cur.x_target() - cur.cell().xo(cur.bv()));
818         return true;
819 }
820
821
822 bool InsetMathGrid::idxBackward(Cursor & cur) const
823 {
824         // leave matrix if at the front edge
825         if (cur.col() == 0)
826                 return false;
827         --cur.idx();
828         cur.pos() = cur.lastpos();
829         return true;
830 }
831
832
833 bool InsetMathGrid::idxForward(Cursor & cur) const
834 {
835         // leave matrix if at the back edge
836         if (cur.col() + 1 == ncols())
837                 return false;
838         ++cur.idx();
839         cur.pos() = 0;
840         return true;
841 }
842
843
844 bool InsetMathGrid::idxFirst(Cursor & cur) const
845 {
846         switch (v_align_) {
847                 case 't':
848                         cur.idx() = 0;
849                         break;
850                 case 'b':
851                         cur.idx() = (nrows() - 1) * ncols();
852                         break;
853                 default:
854                         cur.idx() = ((nrows() - 1) / 2) * ncols();
855         }
856         cur.pos() = 0;
857         return true;
858 }
859
860
861 bool InsetMathGrid::idxLast(Cursor & cur) const
862 {
863         switch (v_align_) {
864                 case 't':
865                         cur.idx() = ncols() - 1;
866                         break;
867                 case 'b':
868                         cur.idx() = nargs() - 1;
869                         break;
870                 default:
871                         cur.idx() = ((nrows() - 1) / 2 + 1) * ncols() - 1;
872         }
873         cur.pos() = cur.lastpos();
874         return true;
875 }
876
877
878 bool InsetMathGrid::idxDelete(idx_type & idx)
879 {
880         // nothing to do if we have just one row
881         if (nrows() == 1)
882                 return false;
883
884         // nothing to do if we are in the middle of the last row of the inset
885         if (idx + ncols() > nargs())
886                 return false;
887
888         // try to delete entire sequence of ncols() empty cells if possible
889         for (idx_type i = idx; i < idx + ncols(); ++i)
890                 if (cell(i).size())
891                         return false;
892
893         // move cells if necessary
894         for (idx_type i = index(row(idx), 0); i < idx; ++i)
895                 swap(cell(i), cell(i + ncols()));
896
897         delRow(row(idx));
898
899         if (idx >= nargs())
900                 idx = nargs() - 1;
901
902         // undo effect of Ctrl-Tab (i.e. pull next cell)
903         //if (idx + 1 != nargs())
904         //      cell(idx).swap(cell(idx + 1));
905
906         // we handled the event..
907         return true;
908 }
909
910
911 // reimplement old behaviour when pressing Delete in the last position
912 // of a cell
913 void InsetMathGrid::idxGlue(idx_type idx)
914 {
915         col_type c = col(idx);
916         if (c + 1 == ncols()) {
917                 if (row(idx) + 1 != nrows()) {
918                         for (col_type cc = 0; cc < ncols(); ++cc)
919                                 cell(idx).append(cell(idx + cc + 1));
920                         delRow(row(idx) + 1);
921                 }
922         } else {
923                 cell(idx).append(cell(idx + 1));
924                 for (col_type cc = c + 2; cc < ncols(); ++cc)
925                         cell(idx - c + cc - 1) = cell(idx - c + cc);
926                 cell(idx - c + ncols() - 1).clear();
927         }
928 }
929
930
931 InsetMathGrid::RowInfo const & InsetMathGrid::rowinfo(row_type row) const
932 {
933         return rowinfo_[row];
934 }
935
936
937 InsetMathGrid::RowInfo & InsetMathGrid::rowinfo(row_type row)
938 {
939         return rowinfo_[row];
940 }
941
942
943 bool InsetMathGrid::idxBetween(idx_type idx, idx_type from, idx_type to) const
944 {
945         row_type const ri = row(idx);
946         row_type const r1 = min(row(from), row(to));
947         row_type const r2 = max(row(from), row(to));
948         col_type const ci = col(idx);
949         col_type const c1 = min(col(from), col(to));
950         col_type const c2 = max(col(from), col(to));
951         return r1 <= ri && ri <= r2 && c1 <= ci && ci <= c2;
952 }
953
954
955
956 void InsetMathGrid::normalize(NormalStream & os) const
957 {
958         os << "[grid ";
959         for (row_type row = 0; row < nrows(); ++row) {
960                 os << "[row ";
961                 for (col_type col = 0; col < ncols(); ++col)
962                         os << "[cell " << cell(index(row, col)) << ']';
963                 os << ']';
964         }
965         os << ']';
966 }
967
968
969 void InsetMathGrid::mathmlize(MathStream & os) const
970 {
971         os << MTag("mtable");
972         for (row_type row = 0; row < nrows(); ++row) {
973                 os << MTag("mtr");
974                 for (col_type col = 0; col < ncols(); ++col)
975                         os << cell(index(row, col));
976                 os << ETag("mtr");
977         }
978         os << ETag("mtable");
979 }
980
981
982 void InsetMathGrid::write(WriteStream & os) const
983 {
984         write(os, 0, 0, nrows(), ncols());
985 }
986
987 void InsetMathGrid::write(WriteStream & os,
988                           row_type beg_row, col_type beg_col,
989                           row_type end_row, col_type end_col) const
990 {
991         MathEnsurer ensurer(os, false);
992         docstring eol;
993         for (row_type row = beg_row; row < end_row; ++row) {
994                 os << verboseHLine(rowinfo_[row].lines_);
995                 // don't write & and empty cells at end of line
996                 col_type lastcol = 0;
997                 bool emptyline = true;
998                 for (col_type col = beg_col; col < end_col; ++col)
999                         if (!cell(index(row, col)).empty()) {
1000                                 lastcol = col + 1;
1001                                 emptyline = false;
1002                         }
1003                 for (col_type col = beg_col; col < lastcol; ++col) {
1004                         os << cell(index(row, col));
1005                         if (os.pendingBrace())
1006                                 ModeSpecifier specifier(os, TEXT_MODE);
1007                         os << eocString(col, lastcol);
1008                 }
1009                 eol = eolString(row, emptyline, os.fragile());
1010                 os << eol;
1011                 // append newline only if line wasn't completely empty
1012                 // and this was not the last line in the grid
1013                 if (!emptyline && row + 1 < end_row)
1014                         os << "\n";
1015         }
1016         // @TODO use end_row instead of nrows() ?
1017         docstring const s = verboseHLine(rowinfo_[nrows()].lines_);
1018         if (!s.empty()) {
1019                 if (eol.empty()) {
1020                         if (os.fragile())
1021                                 os << "\\protect";
1022                         os << "\\\\";
1023                 }
1024                 os << s;
1025         }
1026 }
1027
1028
1029 int InsetMathGrid::colsep() const
1030 {
1031         return 6;
1032 }
1033
1034
1035 int InsetMathGrid::rowsep() const
1036 {
1037         return 6;
1038 }
1039
1040
1041 int InsetMathGrid::hlinesep() const
1042 {
1043         return 3;
1044 }
1045
1046
1047 int InsetMathGrid::vlinesep() const
1048 {
1049         return 3;
1050 }
1051
1052
1053 int InsetMathGrid::border() const
1054 {
1055         return 1;
1056 }
1057
1058
1059 void InsetMathGrid::splitCell(Cursor & cur)
1060 {
1061         if (cur.idx() == cur.lastidx())
1062                 return;
1063         MathData ar = cur.cell();
1064         ar.erase(0, cur.pos());
1065         cur.cell().erase(cur.pos(), cur.lastpos());
1066         ++cur.idx();
1067         cur.pos() = 0;
1068         cur.cell().insert(0, ar);
1069 }
1070
1071
1072 void InsetMathGrid::doDispatch(Cursor & cur, FuncRequest & cmd)
1073 {
1074         //lyxerr << "*** InsetMathGrid: request: " << cmd << endl;
1075
1076         Parse::flags parseflg = Parse::QUIET;
1077
1078         switch (cmd.action) {
1079
1080         // insert file functions
1081         case LFUN_LINE_DELETE:
1082                 cur.recordUndoInset();
1083                 //autocorrect_ = false;
1084                 //macroModeClose();
1085                 //if (selection_) {
1086                 //      selDel();
1087                 //      break;
1088                 //}
1089                 if (nrows() > 1)
1090                         delRow(cur.row());
1091                 if (cur.idx() > cur.lastidx())
1092                         cur.idx() = cur.lastidx();
1093                 if (cur.pos() > cur.lastpos())
1094                         cur.pos() = cur.lastpos();
1095                 break;
1096
1097         case LFUN_CELL_SPLIT:
1098                 cur.recordUndo();
1099                 splitCell(cur);
1100                 break;
1101
1102         case LFUN_CELL_BACKWARD:
1103                 // See below.
1104                 cur.setSelection(false);
1105                 if (!idxPrev(cur)) {
1106                         cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
1107                         cur.undispatched();
1108                 }
1109                 break;
1110
1111         case LFUN_CELL_FORWARD:
1112                 // Can't handle selection by additional 'shift' as this is
1113                 // hard bound to LFUN_CELL_BACKWARD
1114                 cur.setSelection(false);
1115                 if (!idxNext(cur)) {
1116                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
1117                         cur.undispatched();
1118                 }
1119                 break;
1120
1121         case LFUN_NEWLINE_INSERT: {
1122                 cur.recordUndoInset();
1123                 row_type const r = cur.row();
1124                 addRow(r);
1125
1126                 // split line
1127                 for (col_type c = col(cur.idx()) + 1; c < ncols(); ++c)
1128                         swap(cell(index(r, c)), cell(index(r + 1, c)));
1129
1130                 // split cell
1131                 splitCell(cur);
1132                 swap(cell(cur.idx()), cell(cur.idx() + ncols() - 1));
1133                 if (cur.idx() > 0)
1134                         --cur.idx();
1135                 cur.pos() = cur.lastpos();
1136
1137                 //mathcursor->normalize();
1138                 //cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
1139                 break;
1140         }
1141
1142         case LFUN_TABULAR_FEATURE: {
1143                 cur.recordUndoInset();
1144                 //lyxerr << "handling tabular-feature " << to_utf8(cmd.argument()) << endl;
1145                 istringstream is(to_utf8(cmd.argument()));
1146                 string s;
1147                 is >> s;
1148                 if (s == "valign-top")
1149                         setVerticalAlignment('t');
1150                 else if (s == "valign-middle")
1151                         setVerticalAlignment('c');
1152                 else if (s == "valign-bottom")
1153                         setVerticalAlignment('b');
1154                 else if (s == "align-left")
1155                         setHorizontalAlignment('l', cur.col());
1156                 else if (s == "align-right")
1157                         setHorizontalAlignment('r', cur.col());
1158                 else if (s == "align-center")
1159                         setHorizontalAlignment('c', cur.col());
1160                 else if (s == "append-row")
1161                         for (int i = 0, n = extractInt(is); i < n; ++i)
1162                                 addRow(cur.row());
1163                 else if (s == "delete-row") {
1164                         cur.clearSelection(); // bug 4323
1165                         for (int i = 0, n = extractInt(is); i < n; ++i) {
1166                                 delRow(cur.row());
1167                                 if (cur.idx() >= nargs())
1168                                         cur.idx() -= ncols();
1169                         }
1170                         cur.pos() = 0; // trick, see below
1171                 }
1172                 else if (s == "copy-row") {
1173                         // Here (as later) we save the cursor col/row
1174                         // in order to restore it after operation.
1175                         row_type const r = cur.row();
1176                         col_type const c = cur.col();
1177                         for (int i = 0, n = extractInt(is); i < n; ++i)
1178                                 copyRow(cur.row());
1179                         cur.idx() = index(r, c);
1180                 }
1181                 else if (s == "swap-row") {
1182                         swapRow(cur.row());
1183                         // Trick to suppress same-idx-means-different-cell
1184                         // assertion crash:
1185                         cur.pos() = 0;
1186                 }
1187                 else if (s == "add-hline-above")
1188                         rowinfo_[cur.row()].lines_++;
1189                 else if (s == "add-hline-below")
1190                         rowinfo_[cur.row()+1].lines_++;
1191                 else if (s == "delete-hline-above")
1192                         rowinfo_[cur.row()].lines_--;
1193                 else if (s == "delete-hline-below")
1194                         rowinfo_[cur.row()+1].lines_--;
1195                 else if (s == "append-column") {
1196                         row_type const r = cur.row();
1197                         col_type const c = cur.col();
1198                         for (int i = 0, n = extractInt(is); i < n; ++i)
1199                                 addCol(cur.col() + 1);
1200                         cur.idx() = index(r, c);
1201                 }
1202                 else if (s == "delete-column") {
1203                         cur.clearSelection(); // bug 4323
1204                         row_type const r = cur.row();
1205                         col_type const c = cur.col();
1206                         for (int i = 0, n = extractInt(is); i < n; ++i)
1207                                 delCol(col(cur.idx()));
1208                         cur.idx() = index(r, min(c, cur.ncols() - 1));
1209                         cur.pos() = 0; // trick, see above
1210                 }
1211                 else if (s == "copy-column") {
1212                         row_type const r = cur.row();
1213                         col_type const c = cur.col();
1214                         copyCol(cur.col());
1215                         cur.idx() = index(r, c);
1216                 }
1217                 else if (s == "swap-column") {
1218                         swapCol(cur.col());
1219                         cur.pos() = 0; // trick, see above
1220                 }
1221                 else if (s == "add-vline-left") {
1222                         colinfo_[cur.col()].lines_++;
1223                         if (!colinfo_[cur.col()].special_.empty())
1224                                 colinfo_[cur.col()].special_ += '|';
1225                 }
1226                 else if (s == "add-vline-right") {
1227                         colinfo_[cur.col()+1].lines_++;
1228                         if (!colinfo_[cur.col()+1].special_.empty())
1229                                 colinfo_[cur.col()+1].special_.insert(0, 1, '|');
1230                 }
1231                 else if (s == "delete-vline-left") {
1232                         colinfo_[cur.col()].lines_--;
1233                         docstring & special = colinfo_[cur.col()].special_;
1234                         if (!special.empty()) {
1235                                 docstring::size_type i = special.rfind('|');
1236                                 LASSERT(i != docstring::npos, /**/);
1237                                 special.erase(i, 1);
1238                         }
1239                 }
1240                 else if (s == "delete-vline-right") {
1241                         colinfo_[cur.col()+1].lines_--;
1242                         docstring & special = colinfo_[cur.col()+1].special_;
1243                         if (!special.empty()) {
1244                                 docstring::size_type i = special.find('|');
1245                                 LASSERT(i != docstring::npos, /**/);
1246                                 special.erase(i, 1);
1247                         }
1248                 }
1249                 else {
1250                         cur.undispatched();
1251                         break;
1252                 }
1253                 // perhaps this should be FINISHED_BACKWARD -- just for clarity?
1254                 lyxerr << "returning FINISHED_LEFT" << endl;
1255                 break;
1256         }
1257
1258         case LFUN_CLIPBOARD_PASTE:
1259                 parseflg |= Parse::VERBATIM;
1260                 // fall through
1261         case LFUN_PASTE: {
1262                 if (cur.currentMode() == TEXT_MODE)
1263                         parseflg |= Parse::TEXTMODE;
1264                 cur.message(_("Paste"));
1265                 cap::replaceSelection(cur);
1266                 docstring topaste;
1267                 if (cmd.argument().empty() && !theClipboard().isInternal())
1268                         topaste = theClipboard().getAsText();
1269                 else {
1270                         idocstringstream is(cmd.argument());
1271                         int n = 0;
1272                         is >> n;
1273                         topaste = cap::selection(n);
1274                 }
1275                 InsetMathGrid grid(1, 1);
1276                 if (!topaste.empty())
1277                         if (topaste.size() == 1
1278                             || !mathed_parse_normal(grid, topaste, parseflg)) {
1279                                 resetGrid(grid);
1280                                 mathed_parse_normal(grid, topaste,
1281                                                 parseflg | Parse::VERBATIM);
1282                         }
1283
1284                 if (grid.nargs() == 1) {
1285                         // single cell/part of cell
1286                         cur.recordUndo();
1287                         cur.cell().insert(cur.pos(), grid.cell(0));
1288                         cur.pos() += grid.cell(0).size();
1289                 } else {
1290                         // multiple cells
1291                         cur.recordUndoInset();
1292                         col_type const numcols =
1293                                 min(grid.ncols(), ncols() - col(cur.idx()));
1294                         row_type const numrows =
1295                                 min(grid.nrows(), nrows() - cur.row());
1296                         for (row_type r = 0; r < numrows; ++r) {
1297                                 for (col_type c = 0; c < numcols; ++c) {
1298                                         idx_type i = index(r + cur.row(), c + col(cur.idx()));
1299                                         cell(i).insert(0, grid.cell(grid.index(r, c)));
1300                                 }
1301                                 // append the left over horizontal cells to the last column
1302                                 idx_type i = index(r + cur.row(), ncols() - 1);
1303                                 for (InsetMath::col_type c = numcols; c < grid.ncols(); ++c)
1304                                         cell(i).append(grid.cell(grid.index(r, c)));
1305                         }
1306                         // append the left over vertical cells to the last _cell_
1307                         idx_type i = nargs() - 1;
1308                         for (row_type r = numrows; r < grid.nrows(); ++r)
1309                                 for (col_type c = 0; c < grid.ncols(); ++c)
1310                                         cell(i).append(grid.cell(grid.index(r, c)));
1311                 }
1312                 cur.clearSelection(); // bug 393
1313                 cur.finishUndo();
1314                 break;
1315         }
1316
1317         case LFUN_LINE_BEGIN_SELECT:
1318         case LFUN_LINE_BEGIN:
1319         case LFUN_WORD_BACKWARD_SELECT:
1320         case LFUN_WORD_BACKWARD:
1321         case LFUN_WORD_LEFT_SELECT:
1322         case LFUN_WORD_LEFT:
1323                 cur.selHandle(cmd.action == LFUN_WORD_BACKWARD_SELECT ||
1324                                 cmd.action == LFUN_WORD_LEFT_SELECT ||
1325                                 cmd.action == LFUN_LINE_BEGIN_SELECT);
1326                 cur.macroModeClose();
1327                 if (cur.pos() != 0) {
1328                         cur.pos() = 0;
1329                 } else if (cur.idx() % cur.ncols() != 0) {
1330                         cur.idx() -= cur.idx() % cur.ncols();
1331                         cur.pos() = 0;
1332                 } else if (cur.idx() != 0) {
1333                         cur.idx() = 0;
1334                         cur.pos() = 0;
1335                 } else {
1336                         cmd = FuncRequest(LFUN_FINISHED_BACKWARD);
1337                         cur.undispatched();
1338                 }
1339                 break;
1340
1341         case LFUN_WORD_FORWARD_SELECT:
1342         case LFUN_WORD_FORWARD:
1343         case LFUN_WORD_RIGHT_SELECT:
1344         case LFUN_WORD_RIGHT:
1345         case LFUN_LINE_END_SELECT:
1346         case LFUN_LINE_END:
1347                 cur.selHandle(cmd.action == LFUN_WORD_FORWARD_SELECT ||
1348                                 cmd.action == LFUN_WORD_RIGHT_SELECT ||
1349                                 cmd.action == LFUN_LINE_END_SELECT);
1350                 cur.macroModeClose();
1351                 cur.clearTargetX();
1352                 if (cur.pos() != cur.lastpos()) {
1353                         cur.pos() = cur.lastpos();
1354                 } else if ((cur.idx() + 1) % cur.ncols() != 0) {
1355                         cur.idx() += cur.ncols() - 1 - cur.idx() % cur.ncols();
1356                         cur.pos() = cur.lastpos();
1357                 } else if (cur.idx() != cur.lastidx()) {
1358                         cur.idx() = cur.lastidx();
1359                         cur.pos() = cur.lastpos();
1360                 } else {
1361                         cmd = FuncRequest(LFUN_FINISHED_FORWARD);
1362                         cur.undispatched();
1363                 }
1364                 break;
1365
1366         default:
1367                 InsetMathNest::doDispatch(cur, cmd);
1368         }
1369 }
1370
1371
1372 bool InsetMathGrid::getStatus(Cursor & cur, FuncRequest const & cmd,
1373                 FuncStatus & status) const
1374 {
1375         switch (cmd.action) {
1376         case LFUN_TABULAR_FEATURE: {
1377                 string const s = cmd.getArg(0);
1378                 if (nrows() <= 1 && (s == "delete-row" || s == "swap-row")) {
1379                         status.setEnabled(false);
1380                         status.message(from_utf8(N_("Only one row")));
1381                         return true;
1382                 }
1383                 if (ncols() <= 1 &&
1384                     (s == "delete-column" || s == "swap-column")) {
1385                         status.setEnabled(false);
1386                         status.message(from_utf8(N_("Only one column")));
1387                         return true;
1388                 }
1389                 if ((rowinfo_[cur.row()].lines_ == 0 &&
1390                      s == "delete-hline-above") ||
1391                     (rowinfo_[cur.row() + 1].lines_ == 0 &&
1392                      s == "delete-hline-below")) {
1393                         status.setEnabled(false);
1394                         status.message(from_utf8(N_("No hline to delete")));
1395                         return true;
1396                 }
1397
1398                 if ((colinfo_[cur.col()].lines_ == 0 &&
1399                      s == "delete-vline-left") ||
1400                     (colinfo_[cur.col() + 1].lines_ == 0 &&
1401                      s == "delete-vline-right")) {
1402                         status.setEnabled(false);
1403                         status.message(from_utf8(N_("No vline to delete")));
1404                         return true;
1405                 }
1406                 if (s == "valign-top" || s == "valign-middle" ||
1407                     s == "valign-bottom" || s == "align-left" ||
1408                     s == "align-right" || s == "align-center") {
1409                         status.setEnabled(true);
1410                         char const ha = horizontalAlignment(cur.col());
1411                         char const va = verticalAlignment();
1412                         status.setOnOff((s == "align-left" && ha == 'l')
1413                                         || (s == "align-right"   && ha == 'r')
1414                                         || (s == "align-center"  && ha == 'c')
1415                                         || (s == "valign-top"    && va == 't')
1416                                         || (s == "valign-bottom" && va == 'b')
1417                                         || (s == "valign-middle" && va == 'm'));
1418                 }
1419                 if (s == "append-row" || s == "delete-row" ||
1420                     s == "copy-row" || s == "swap-row" ||
1421                     s == "add-hline-above" || s == "add-hline-below" ||
1422                     s == "delete-hline-above" || s == "delete-hline-below" ||
1423                     s == "append-column" || s == "delete-column" ||
1424                     s == "copy-column" || s == "swap-column" ||
1425                     s == "add-vline-left" || s == "add-vline-right" ||
1426                     s == "delete-vline-left" || s == "delete-vline-right")
1427                         status.setEnabled(true);
1428                 else {
1429                         status.setEnabled(false);
1430                         status.message(bformat(
1431                                 from_utf8(N_("Unknown tabular feature '%1$s'")), lyx::from_ascii(s)));
1432                 }
1433
1434 #if 0
1435                 // FIXME: What did this code do?
1436                 // Please check whether it is still needed!
1437                 // should be more precise
1438                 if (v_align_ == '\0') {
1439                         status.enable(true);
1440                         break;
1441                 }
1442                 if (cmd.argument().empty()) {
1443                         status.enable(false);
1444                         break;
1445                 }
1446                 if (!contains("tcb", cmd.argument()[0])) {
1447                         status.enable(false);
1448                         break;
1449                 }
1450                 status.setOnOff(cmd.argument()[0] == v_align_);
1451                 status.setEnabled(true);
1452 #endif
1453                 return true;
1454         }
1455
1456         case LFUN_CELL_SPLIT:
1457                 status.setEnabled(true);
1458                 return true;
1459
1460         case LFUN_CELL_BACKWARD:
1461         case LFUN_CELL_FORWARD:
1462                 status.setEnabled(true);
1463                 return true;
1464
1465         default:
1466                 return InsetMathNest::getStatus(cur, cmd, status);
1467         }
1468 }
1469
1470
1471 } // namespace lyx