]> git.lyx.org Git - lyx.git/blob - src/mathed/math_gridinset.C
Ensure all #warning statements are wrapped by #ifdef WITH_WARNINGS.
[lyx.git] / src / mathed / math_gridinset.C
1 /**
2  * \file math_gridinset.C
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
13 #include "math_gridinset.h"
14 #include "math_data.h"
15 #include "math_mathmlstream.h"
16 #include "math_streamstr.h"
17
18 #include "BufferView.h"
19 #include "CutAndPaste.h"
20 #include "FuncStatus.h"
21 #include "LColor.h"
22 #include "cursor.h"
23 #include "debug.h"
24 #include "funcrequest.h"
25 #include "undo.h"
26
27 #include "frontends/Painter.h"
28
29 #include "support/std_sstream.h"
30
31 #include "insets/mailinset.h"
32
33 using std::endl;
34 using std::max;
35 using std::min;
36 using std::swap;
37
38 using std::string;
39 using std::auto_ptr;
40 using std::istream;
41 using std::istringstream;
42 using std::ostringstream;
43 using std::vector;
44
45
46 class GridInsetMailer : public MailInset {
47 public:
48         GridInsetMailer(MathGridInset & inset) : inset_(inset) {}
49         ///
50         virtual string const & name() const
51         {
52                 static string const theName = "tabular";
53                 return theName;
54         }
55         ///
56         virtual string const inset2string(Buffer const &) const
57         {
58                 ostringstream data;
59                 //data << name() << " active_cell " << inset.getActCell() << '\n';
60                 data << name() << " active_cell " << 0 << '\n';
61                 WriteStream ws(data);
62                 inset_.write(ws);
63                 return data.str();
64         }
65
66 protected:
67         InsetBase & inset() const { return inset_; }
68         MathGridInset & inset_;
69 };
70
71
72 void mathed_parse_normal(MathGridInset &, string const & argument);
73
74 namespace {
75
76 string verboseHLine(int n)
77 {
78         string res;
79         for (int i = 0; i < n; ++i)
80                 res += "\\hline";
81         if (n)
82                 res += ' ';
83         return res;
84 }
85
86
87 int extractInt(istream & is)
88 {
89         int num = 1;
90         is >> num;
91         return (num == 0) ? 1 : num;
92 }
93
94 }
95
96
97 //////////////////////////////////////////////////////////////
98
99
100 MathGridInset::CellInfo::CellInfo()
101         : dummy_(false)
102 {}
103
104
105
106
107 //////////////////////////////////////////////////////////////
108
109
110 MathGridInset::RowInfo::RowInfo()
111         : lines_(0), skip_(0)
112 {}
113
114
115
116 int MathGridInset::RowInfo::skipPixels() const
117 {
118         return crskip_.inBP();
119 }
120
121
122
123 //////////////////////////////////////////////////////////////
124
125
126 MathGridInset::ColInfo::ColInfo()
127         : align_('c'), leftline_(false), rightline_(false), lines_(0)
128 {}
129
130
131 //////////////////////////////////////////////////////////////
132
133
134 MathGridInset::MathGridInset(char v, string const & h)
135         : MathNestInset(guessColumns(h)),
136           rowinfo_(2),
137           colinfo_(guessColumns(h) + 1),
138           cellinfo_(1 * guessColumns(h))
139 {
140         setDefaults();
141         valign(v);
142         halign(h);
143         //lyxerr << "created grid with " << ncols() << " columns" << endl;
144 }
145
146
147 MathGridInset::MathGridInset()
148         : MathNestInset(1),
149           rowinfo_(1 + 1),
150                 colinfo_(1 + 1),
151                 cellinfo_(1),
152                 v_align_('c')
153 {
154         setDefaults();
155 }
156
157
158 MathGridInset::MathGridInset(col_type m, row_type n)
159         : MathNestInset(m * n),
160           rowinfo_(n + 1),
161                 colinfo_(m + 1),
162                 cellinfo_(m * n),
163                 v_align_('c')
164 {
165         setDefaults();
166 }
167
168
169 MathGridInset::MathGridInset(col_type m, row_type n, char v, string const & h)
170         : MathNestInset(m * n),
171           rowinfo_(n + 1),
172           colinfo_(m + 1),
173                 cellinfo_(m * n),
174                 v_align_(v)
175 {
176         setDefaults();
177         valign(v);
178         halign(h);
179 }
180
181
182 MathGridInset::~MathGridInset()
183 {
184         GridInsetMailer mailer(*this);
185         mailer.hideDialog();
186 }
187
188
189 auto_ptr<InsetBase> MathGridInset::clone() const
190 {
191         return auto_ptr<InsetBase>(new MathGridInset(*this));
192 }
193
194
195 MathInset::idx_type MathGridInset::index(row_type row, col_type col) const
196 {
197         return col + ncols() * row;
198 }
199
200
201 void MathGridInset::setDefaults()
202 {
203         if (ncols() <= 0)
204                 lyxerr << "positive number of columns expected" << endl;
205         //if (nrows() <= 0)
206         //      lyxerr << "positive number of rows expected" << endl;
207         for (col_type col = 0; col < ncols(); ++col) {
208                 colinfo_[col].align_ = defaultColAlign(col);
209                 colinfo_[col].skip_  = defaultColSpace(col);
210         }
211 }
212
213
214 void MathGridInset::halign(string const & hh)
215 {
216         col_type col = 0;
217         for (string::const_iterator it = hh.begin(); it != hh.end(); ++it) {
218                 if (col >= ncols())
219                         break;
220                 char c = *it;
221                 if (c == '|') {
222                         colinfo_[col].lines_++;
223                 } else if (c == 'c' || c == 'l' || c == 'r') {
224                         colinfo_[col].align_ = c;
225                         ++col;
226                         colinfo_[col].lines_ = 0;
227                 } else {
228                         lyxerr << "unknown column separator: '" << c << "'" << endl;
229                 }
230         }
231
232 /*
233         col_type n = hh.size();
234         if (n > ncols())
235                 n = ncols();
236         for (col_type col = 0; col < n; ++col)
237                 colinfo_[col].align_ = hh[col];
238 */
239 }
240
241
242 MathGridInset::col_type MathGridInset::guessColumns(string const & hh) const
243 {
244         col_type col = 0;
245         for (string::const_iterator it = hh.begin(); it != hh.end(); ++it)
246                 if (*it == 'c' || *it == 'l' || *it == 'r')
247                         ++col;
248         // let's have at least one column, even if we did not recognize its
249         // alignment
250         if (col == 0)
251                 col = 1;
252         return col;
253 }
254
255
256 void MathGridInset::halign(char h, col_type col)
257 {
258         colinfo_[col].align_ = h;
259 }
260
261
262 char MathGridInset::halign(col_type col) const
263 {
264         return colinfo_[col].align_;
265 }
266
267
268 string MathGridInset::halign() const
269 {
270         string res;
271         for (col_type col = 0; col < ncols(); ++col) {
272                 res += string(colinfo_[col].lines_, '|');
273                 res += colinfo_[col].align_;
274         }
275         return res + string(colinfo_[ncols()].lines_, '|');
276 }
277
278
279 void MathGridInset::valign(char c)
280 {
281         v_align_ = c;
282 }
283
284
285 char MathGridInset::valign() const
286 {
287         return v_align_;
288 }
289
290
291 MathGridInset::col_type MathGridInset::ncols() const
292 {
293         return colinfo_.size() - 1;
294 }
295
296
297 MathGridInset::row_type MathGridInset::nrows() const
298 {
299         return rowinfo_.size() - 1;
300 }
301
302
303 MathGridInset::col_type MathGridInset::col(idx_type idx) const
304 {
305         return idx % ncols();
306 }
307
308
309 MathGridInset::row_type MathGridInset::row(idx_type idx) const
310 {
311         return idx / ncols();
312 }
313
314
315 void MathGridInset::vcrskip(LyXLength const & crskip, row_type row)
316 {
317         rowinfo_[row].crskip_ = crskip;
318 }
319
320
321 LyXLength MathGridInset::vcrskip(row_type row) const
322 {
323         return rowinfo_[row].crskip_;
324 }
325
326
327 void MathGridInset::metrics(MetricsInfo & mi) const
328 {
329         // let the cells adjust themselves
330         MathNestInset::metrics(mi);
331
332         // compute absolute sizes of vertical structure
333         for (row_type row = 0; row < nrows(); ++row) {
334                 int asc  = 0;
335                 int desc = 0;
336                 for (col_type col = 0; col < ncols(); ++col) {
337                         MathArray const & c = cell(index(row, col));
338                         asc  = max(asc,  c.ascent());
339                         desc = max(desc, c.descent());
340                 }
341                 rowinfo_[row].ascent_  = asc;
342                 rowinfo_[row].descent_ = desc;
343         }
344         rowinfo_[0].ascent_       += hlinesep() * rowinfo_[0].lines_;
345         rowinfo_[nrows()].ascent_  = 0;
346         rowinfo_[nrows()].descent_ = 0;
347
348         // compute vertical offsets
349         rowinfo_[0].offset_ = 0;
350         for (row_type row = 1; row <= nrows(); ++row) {
351                 rowinfo_[row].offset_  =
352                         rowinfo_[row - 1].offset_  +
353                         rowinfo_[row - 1].descent_ +
354                         rowinfo_[row - 1].skipPixels() +
355                         rowsep() +
356                         rowinfo_[row].lines_ * hlinesep() +
357                         rowinfo_[row].ascent_;
358         }
359
360         // adjust vertical offset
361         int h = 0;
362         switch (v_align_) {
363                 case 't':
364                         h = 0;
365                         break;
366                 case 'b':
367                         h = rowinfo_[nrows() - 1].offset_;
368                         break;
369                 default:
370                         h = rowinfo_[nrows() - 1].offset_ / 2;
371         }
372         for (row_type row = 0; row <= nrows(); ++row)
373                 rowinfo_[row].offset_ -= h;
374
375
376         // compute absolute sizes of horizontal structure
377         for (col_type col = 0; col < ncols(); ++col) {
378                 int wid = 0;
379                 for (row_type row = 0; row < nrows(); ++row)
380                         wid = max(wid, cell(index(row, col)).width());
381                 colinfo_[col].width_ = wid;
382         }
383         colinfo_[ncols()].width_  = 0;
384
385         // compute horizontal offsets
386         colinfo_[0].offset_ = border();
387         for (col_type col = 1; col <= ncols(); ++col) {
388                 colinfo_[col].offset_ =
389                         colinfo_[col - 1].offset_ +
390                         colinfo_[col - 1].width_ +
391                         colinfo_[col - 1].skip_ +
392                         colsep() +
393                         colinfo_[col].lines_ * vlinesep();
394         }
395
396
397         dim_.wid   =   colinfo_[ncols() - 1].offset_
398                        + colinfo_[ncols() - 1].width_
399                  + vlinesep() * colinfo_[ncols()].lines_
400                        + border();
401
402         dim_.asc  = - rowinfo_[0].offset_
403                        + rowinfo_[0].ascent_
404                  + hlinesep() * rowinfo_[0].lines_
405                        + border();
406
407         dim_.des =   rowinfo_[nrows() - 1].offset_
408                        + rowinfo_[nrows() - 1].descent_
409                  + hlinesep() * rowinfo_[nrows()].lines_
410                        + border();
411
412
413 /*
414         // Increase ws_[i] for 'R' columns (except the first one)
415         for (int i = 1; i < nc_; ++i)
416                 if (align_[i] == 'R')
417                         ws_[i] += 10 * df_width;
418         // Increase ws_[i] for 'C' column
419         if (align_[0] == 'C')
420                 if (ws_[0] < 7 * workwidth / 8)
421                         ws_[0] = 7 * workwidth / 8;
422
423         // Adjust local tabs
424         width = colsep();
425         for (cxrow = row_.begin(); cxrow; ++cxrow) {
426                 int rg = COLSEP;
427                 int lf = 0;
428                 for (int i = 0; i < nc_; ++i) {
429                         bool isvoid = false;
430                         if (cxrow->getTab(i) <= 0) {
431                                 cxrow->setTab(i, df_width);
432                                 isvoid = true;
433                         }
434                         switch (align_[i]) {
435                         case 'l':
436                                 lf = 0;
437                                 break;
438                         case 'c':
439                                 lf = (ws_[i] - cxrow->getTab(i))/2;
440                                 break;
441                         case 'r':
442                         case 'R':
443                                 lf = ws_[i] - cxrow->getTab(i);
444                                 break;
445                         case 'C':
446                                 if (cxrow == row_.begin())
447                                         lf = 0;
448                                 else if (cxrow.is_last())
449                                         lf = ws_[i] - cxrow->getTab(i);
450                                 else
451                                         lf = (ws_[i] - cxrow->getTab(i))/2;
452                                 break;
453                         }
454                         int const ww = (isvoid) ? lf : lf + cxrow->getTab(i);
455                         cxrow->setTab(i, lf + rg);
456                         rg = ws_[i] - ww + colsep();
457                         if (cxrow == row_.begin())
458                                 width += ws_[i] + colsep();
459                 }
460                 cxrow->setBaseline(cxrow->getBaseline() - ascent);
461         }
462 */
463         metricsMarkers2(dim_);
464 }
465
466
467 void MathGridInset::metrics(MetricsInfo & mi, Dimension & dim) const
468 {
469         metrics(mi);
470         dim = dim_;
471 }
472
473
474 void MathGridInset::draw(PainterInfo & pi, int x, int y) const
475 {
476         for (idx_type idx = 0; idx < nargs(); ++idx)
477                 cell(idx).draw(pi, x + cellXOffset(idx), y + cellYOffset(idx));
478
479         for (row_type row = 0; row <= nrows(); ++row)
480                 for (int i = 0; i < rowinfo_[row].lines_; ++i) {
481                         int yy = y + rowinfo_[row].offset_ - rowinfo_[row].ascent_
482                                 - i * hlinesep() - hlinesep()/2 - rowsep()/2;
483                         pi.pain.line(x + 1, yy,
484                                      x + dim_.width() - 1, yy,
485                                      LColor::foreground);
486                 }
487
488         for (col_type col = 0; col <= ncols(); ++col)
489                 for (int i = 0; i < colinfo_[col].lines_; ++i) {
490                         int xx = x + colinfo_[col].offset_
491                                 - i * vlinesep() - vlinesep()/2 - colsep()/2;
492                         pi.pain.line(xx, y - dim_.ascent() + 1,
493                                      xx, y + dim_.descent() - 1,
494                                      LColor::foreground);
495                 }
496         drawMarkers2(pi, x, y);
497 }
498
499
500 void MathGridInset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
501 {
502         // let the cells adjust themselves
503         //MathNestInset::metrics(mi);
504         for (idx_type i = 0; i < nargs(); ++i)
505                 cell(i).metricsT(mi, dim);
506
507         // compute absolute sizes of vertical structure
508         for (row_type row = 0; row < nrows(); ++row) {
509                 int asc  = 0;
510                 int desc = 0;
511                 for (col_type col = 0; col < ncols(); ++col) {
512                         MathArray const & c = cell(index(row, col));
513                         asc  = max(asc,  c.ascent());
514                         desc = max(desc, c.descent());
515                 }
516                 rowinfo_[row].ascent_  = asc;
517                 rowinfo_[row].descent_ = desc;
518         }
519         //rowinfo_[0].ascent_       += hlinesep() * rowinfo_[0].lines_;
520         rowinfo_[nrows()].ascent_  = 0;
521         rowinfo_[nrows()].descent_ = 0;
522
523         // compute vertical offsets
524         rowinfo_[0].offset_ = 0;
525         for (row_type row = 1; row <= nrows(); ++row) {
526                 rowinfo_[row].offset_  =
527                         rowinfo_[row - 1].offset_  +
528                         rowinfo_[row - 1].descent_ +
529                         //rowinfo_[row - 1].skipPixels() +
530                         1 + //rowsep() +
531                         //rowinfo_[row].lines_ * hlinesep() +
532                         rowinfo_[row].ascent_;
533         }
534
535         // adjust vertical offset
536         int h = 0;
537         switch (v_align_) {
538                 case 't':
539                         h = 0;
540                         break;
541                 case 'b':
542                         h = rowinfo_[nrows() - 1].offset_;
543                         break;
544                 default:
545                         h = rowinfo_[nrows() - 1].offset_ / 2;
546         }
547         for (row_type row = 0; row <= nrows(); ++row)
548                 rowinfo_[row].offset_ -= h;
549
550
551         // compute absolute sizes of horizontal structure
552         for (col_type col = 0; col < ncols(); ++col) {
553                 int wid = 0;
554                 for (row_type row = 0; row < nrows(); ++row)
555                         wid = max(wid, cell(index(row, col)).width());
556                 colinfo_[col].width_ = wid;
557         }
558         colinfo_[ncols()].width_  = 0;
559
560         // compute horizontal offsets
561         colinfo_[0].offset_ = border();
562         for (col_type col = 1; col <= ncols(); ++col) {
563                 colinfo_[col].offset_ =
564                         colinfo_[col - 1].offset_ +
565                         colinfo_[col - 1].width_ +
566                         colinfo_[col - 1].skip_ +
567                         1 ; //colsep() +
568                         //colinfo_[col].lines_ * vlinesep();
569         }
570
571
572         dim.wid  =  colinfo_[ncols() - 1].offset_
573                        + colinfo_[ncols() - 1].width_
574                  //+ vlinesep() * colinfo_[ncols()].lines_
575                        + 2;
576
577         dim.asc  = -rowinfo_[0].offset_
578                        + rowinfo_[0].ascent_
579                  //+ hlinesep() * rowinfo_[0].lines_
580                        + 1;
581
582         dim.des  =  rowinfo_[nrows() - 1].offset_
583                        + rowinfo_[nrows() - 1].descent_
584                  //+ hlinesep() * rowinfo_[nrows()].lines_
585                        + 1;
586 }
587
588
589 void MathGridInset::drawT(TextPainter & pain, int x, int y) const
590 {
591         for (idx_type idx = 0; idx < nargs(); ++idx)
592                 cell(idx).drawT(pain, x + cellXOffset(idx), y + cellYOffset(idx));
593 }
594
595
596 string MathGridInset::eolString(row_type row, bool fragile) const
597 {
598         string eol;
599
600         if (!rowinfo_[row].crskip_.zero())
601                 eol += '[' + rowinfo_[row].crskip_.asLatexString() + ']';
602
603         // make sure an upcoming '[' does not break anything
604         if (row + 1 < nrows()) {
605                 MathArray const & c = cell(index(row + 1, 0));
606                 if (c.size() && c.front()->getChar() == '[')
607                         //eol += "[0pt]";
608                         eol += "{}";
609         }
610
611         // only add \\ if necessary
612         if (eol.empty() && row + 1 == nrows())
613                 return string();
614
615         return (fragile ? "\\protect\\\\" : "\\\\") + eol;
616 }
617
618
619 string MathGridInset::eocString(col_type col, col_type lastcol) const
620 {
621         if (col + 1 == lastcol)
622                 return string();
623         return " & ";
624 }
625
626
627 void MathGridInset::addRow(row_type row)
628 {
629         rowinfo_.insert(rowinfo_.begin() + row + 1, RowInfo());
630         cells_.insert
631                 (cells_.begin() + (row + 1) * ncols(), ncols(), MathArray());
632         cellinfo_.insert
633                 (cellinfo_.begin() + (row + 1) * ncols(), ncols(), CellInfo());
634 }
635
636
637 void MathGridInset::appendRow()
638 {
639         rowinfo_.push_back(RowInfo());
640         //cells_.insert(cells_.end(), ncols(), MathArray());
641         for (col_type col = 0; col < ncols(); ++col) {
642                 cells_.push_back(cells_type::value_type());
643                 cellinfo_.push_back(CellInfo());
644         }
645 }
646
647
648 void MathGridInset::delRow(row_type row)
649 {
650         if (nrows() == 1)
651                 return;
652
653         cells_type::iterator it = cells_.begin() + row * ncols();
654         cells_.erase(it, it + ncols());
655
656         vector<CellInfo>::iterator jt = cellinfo_.begin() + row * ncols();
657         cellinfo_.erase(jt, jt + ncols());
658
659         rowinfo_.erase(rowinfo_.begin() + row);
660 }
661
662
663 void MathGridInset::copyRow(row_type row)
664 {
665         addRow(row);
666         for (col_type col = 0; col < ncols(); ++col)
667                 cells_[(row + 1) * ncols() + col] = cells_[row * ncols() + col];
668 }
669
670
671 void MathGridInset::swapRow(row_type row)
672 {
673         if (nrows() == 1)
674                 return;
675         if (row + 1 == nrows())
676                 --row;
677         for (col_type col = 0; col < ncols(); ++col)
678                 swap(cells_[row * ncols() + col], cells_[(row + 1) * ncols() + col]);
679 }
680
681
682 void MathGridInset::addCol(col_type newcol)
683 {
684         const col_type nc = ncols();
685         const row_type nr = nrows();
686         cells_type new_cells((nc + 1) * nr);
687         vector<CellInfo> new_cellinfo((nc + 1) * nr);
688
689         for (row_type row = 0; row < nr; ++row)
690                 for (col_type col = 0; col < nc; ++col) {
691                         new_cells[row * (nc + 1) + col + (col > newcol)]
692                                 = cells_[row * nc + col];
693                         new_cellinfo[row * (nc + 1) + col + (col > newcol)]
694                                 = cellinfo_[row * nc + col];
695                 }
696         swap(cells_, new_cells);
697         swap(cellinfo_, new_cellinfo);
698
699         ColInfo inf;
700         inf.skip_  = defaultColSpace(newcol);
701         inf.align_ = defaultColAlign(newcol);
702         colinfo_.insert(colinfo_.begin() + newcol, inf);
703 }
704
705
706 void MathGridInset::delCol(col_type col)
707 {
708         if (ncols() == 1)
709                 return;
710
711         cells_type tmpcells;
712         vector<CellInfo> tmpcellinfo;
713         for (col_type i = 0; i < nargs(); ++i)
714                 if (i % ncols() != col) {
715                         tmpcells.push_back(cells_[i]);
716                         tmpcellinfo.push_back(cellinfo_[i]);
717                 }
718         swap(cells_, tmpcells);
719         swap(cellinfo_, tmpcellinfo);
720
721         colinfo_.erase(colinfo_.begin() + col);
722 }
723
724
725 void MathGridInset::copyCol(col_type col)
726 {
727         addCol(col);
728         for (row_type row = 0; row < nrows(); ++row)
729                 cells_[row * ncols() + col + 1] = cells_[row * ncols() + col];
730 }
731
732
733 void MathGridInset::swapCol(col_type col)
734 {
735         if (ncols() == 1)
736                 return;
737         if (col + 1 == ncols())
738                 --col;
739         for (row_type row = 0; row < nrows(); ++row)
740                 swap(cells_[row * ncols() + col], cells_[row * ncols() + col + 1]);
741 }
742
743
744 int MathGridInset::cellXOffset(idx_type idx) const
745 {
746         col_type c = col(idx);
747         int x = colinfo_[c].offset_;
748         char align = colinfo_[c].align_;
749         if (align == 'r' || align == 'R')
750                 x += colinfo_[c].width_ - cell(idx).width();
751         if (align == 'c' || align == 'C')
752                 x += (colinfo_[c].width_ - cell(idx).width()) / 2;
753         return x;
754 }
755
756
757 int MathGridInset::cellYOffset(idx_type idx) const
758 {
759         return rowinfo_[row(idx)].offset_;
760 }
761
762
763 bool MathGridInset::idxUpDown(LCursor & cur, bool up) const
764 {
765         if (up) {
766                 if (cur.idx() < ncols())
767                         return false;
768                 cur.idx() -= ncols();
769         } else {
770                 if (cur.idx() >= ncols() * (nrows() - 1))
771                         return false;
772                 cur.idx() += ncols();
773         }
774         cur.pos() = cur.cell().x2pos(cur.x_target() - cur.cell().xo());
775         return true;
776 }
777
778
779 bool MathGridInset::idxLeft(LCursor & cur) const
780 {
781         // leave matrix if on the left hand edge
782         if (cur.col() == 0)
783                 return false;
784         --cur.idx();
785         cur.pos() = cur.lastpos();
786         return true;
787 }
788
789
790 bool MathGridInset::idxRight(LCursor & cur) const
791 {
792         // leave matrix if on the right hand edge
793         if (cur.col() + 1 == ncols())
794                 return false;
795         ++cur.idx();
796         cur.pos() = 0;
797         return true;
798 }
799
800
801 bool MathGridInset::idxFirst(LCursor & cur) const
802 {
803         switch (v_align_) {
804                 case 't':
805                         cur.idx() = 0;
806                         break;
807                 case 'b':
808                         cur.idx() = (nrows() - 1) * ncols();
809                         break;
810                 default:
811                         cur.idx() = ((nrows() - 1) / 2) * ncols();
812         }
813         cur.pos() = 0;
814         return true;
815 }
816
817
818 bool MathGridInset::idxLast(LCursor & cur) const
819 {
820         switch (v_align_) {
821                 case 't':
822                         cur.idx() = ncols() - 1;
823                         break;
824                 case 'b':
825                         cur.idx() = nargs() - 1;
826                         break;
827                 default:
828                         cur.idx() = ((nrows() - 1) / 2 + 1) * ncols() - 1;
829         }
830         cur.pos() = cur.lastpos();
831         return true;
832 }
833
834
835 bool MathGridInset::idxDelete(idx_type & idx)
836 {
837         // nothing to do if we have just one row
838         if (nrows() == 1)
839                 return false;
840
841         // nothing to do if we are in the middle of the last row of the inset
842         if (idx + ncols() > nargs())
843                 return false;
844
845         // try to delete entire sequence of ncols() empty cells if possible
846         for (idx_type i = idx; i < idx + ncols(); ++i)
847                 if (cell(i).size())
848                         return false;
849
850         // move cells if necessary
851         for (idx_type i = index(row(idx), 0); i < idx; ++i)
852                 swap(cell(i), cell(i + ncols()));
853
854         delRow(row(idx));
855
856         if (idx >= nargs())
857                 idx = nargs() - 1;
858
859         // undo effect of Ctrl-Tab (i.e. pull next cell)
860         //if (idx + 1 != nargs())
861         //      cell(idx).swap(cell(idx + 1));
862
863         // we handled the event..
864         return true;
865 }
866
867
868 // reimplement old behaviour when pressing Delete in the last position
869 // of a cell
870 void MathGridInset::idxGlue(idx_type idx)
871 {
872         col_type c = col(idx);
873         if (c + 1 == ncols()) {
874                 if (row(idx) + 1 != nrows()) {
875                         for (col_type cc = 0; cc < ncols(); ++cc)
876                                 cell(idx).append(cell(idx + cc + 1));
877                         delRow(row(idx) + 1);
878                 }
879         } else {
880                 cell(idx).append(cell(idx + 1));
881                 for (col_type cc = c + 2; cc < ncols(); ++cc)
882                         cell(idx - c + cc - 1) = cell(idx - c + cc);
883                 cell(idx - c + ncols() - 1).clear();
884         }
885 }
886
887
888 MathGridInset::RowInfo const & MathGridInset::rowinfo(row_type row) const
889 {
890         return rowinfo_[row];
891 }
892
893
894 MathGridInset::RowInfo & MathGridInset::rowinfo(row_type row)
895 {
896         return rowinfo_[row];
897 }
898
899
900 bool MathGridInset::idxBetween(idx_type idx, idx_type from, idx_type to) const
901 {
902         row_type const ri = row(idx);
903         row_type const r1 = min(row(from), row(to));
904         row_type const r2 = max(row(from), row(to));
905         col_type const ci = col(idx);
906         col_type const c1 = min(col(from), col(to));
907         col_type const c2 = max(col(from), col(to));
908         return r1 <= ri && ri <= r2 && c1 <= ci && ci <= c2;
909 }
910
911
912
913 void MathGridInset::normalize(NormalStream & os) const
914 {
915         os << "[grid ";
916         for (row_type row = 0; row < nrows(); ++row) {
917                 os << "[row ";
918                 for (col_type col = 0; col < ncols(); ++col)
919                         os << "[cell " << cell(index(row, col)) << ']';
920                 os << ']';
921         }
922         os << ']';
923 }
924
925
926 void MathGridInset::mathmlize(MathMLStream & os) const
927 {
928         os << MTag("mtable");
929         for (row_type row = 0; row < nrows(); ++row) {
930                 os << MTag("mtr");
931                 for (col_type col = 0; col < ncols(); ++col)
932                         os << cell(index(row, col));
933                 os << ETag("mtr");
934         }
935         os << ETag("mtable");
936 }
937
938
939 void MathGridInset::write(WriteStream & os) const
940 {
941         for (row_type row = 0; row < nrows(); ++row) {
942                 os << verboseHLine(rowinfo_[row].lines_);
943                 // don't write & and empty cells at end of line
944                 col_type lastcol = 0;
945                 bool emptyline = true;
946                 for (col_type col = 0; col < ncols(); ++col)
947                         if (!cell(index(row, col)).empty()) {
948                                 lastcol = col + 1;
949                                 emptyline = false;
950                         }
951                 for (col_type col = 0; col < lastcol; ++col)
952                         os << cell(index(row, col)) << eocString(col, lastcol);
953                 os << eolString(row, os.fragile());
954                 // append newline only if line wasn't completely empty
955                 // and this was not the last line in the grid
956                 if (!emptyline && row + 1 < nrows())
957                         os << "\n";
958         }
959         string const s = verboseHLine(rowinfo_[nrows()].lines_);
960         if (!s.empty() && s != " ") {
961                 if (os.fragile())
962                         os << "\\protect";
963                 os << "\\\\" << s;
964         }
965 }
966
967
968 int MathGridInset::colsep() const
969 {
970         return 6;
971 }
972
973
974 int MathGridInset::rowsep() const
975 {
976         return 6;
977 }
978
979
980 int MathGridInset::hlinesep() const
981 {
982         return 3;
983 }
984
985
986 int MathGridInset::vlinesep() const
987 {
988         return 3;
989 }
990
991
992 int MathGridInset::border() const
993 {
994         return 1;
995 }
996
997
998 void MathGridInset::splitCell(LCursor & cur)
999 {
1000         if (cur.idx() == cur.lastidx())
1001                 return;
1002         MathArray ar = cur.cell();
1003         ar.erase(0, cur.pos());
1004         cur.cell().erase(cur.pos(), cur.lastpos());
1005         ++cur.idx();
1006         cur.pos() = 0;
1007         cur.cell().insert(0, ar);
1008 }
1009
1010
1011 void MathGridInset::priv_dispatch(LCursor & cur, FuncRequest & cmd)
1012 {
1013         //lyxerr << "*** MathGridInset: request: " << cmd << endl;
1014         switch (cmd.action) {
1015
1016         case LFUN_MOUSE_RELEASE:
1017                 //if (cmd.button() == mouse_button::button3) {
1018                 //      GridInsetMailer(*this).showDialog();
1019                 //      return DispatchResult(true, true);
1020                 //}
1021                 MathNestInset::priv_dispatch(cur, cmd);
1022                 break;
1023
1024         case LFUN_INSET_DIALOG_UPDATE:
1025                 GridInsetMailer(*this).updateDialog(&cur.bv());
1026                 break;
1027
1028         // insert file functions
1029         case LFUN_DELETE_LINE_FORWARD:
1030                 recordUndo(cur);
1031                 //autocorrect_ = false;
1032                 //macroModeClose();
1033                 //if (selection_) {
1034                 //      selDel();
1035                 //      break;
1036                 //}
1037                 if (nrows() > 1)
1038                         delRow(cur.row());
1039                 if (cur.idx() > cur.lastidx())
1040                         cur.idx() = cur.lastidx();
1041                 if (cur.pos() > cur.lastpos())
1042                         cur.pos() = cur.lastpos();
1043                 break;
1044
1045         case LFUN_CELL_SPLIT:
1046                 recordUndo(cur);
1047                 splitCell(cur);
1048                 break;
1049
1050         case LFUN_BREAKLINE: {
1051                 recordUndo(cur);
1052                 row_type const r = cur.row();
1053                 addRow(r);
1054
1055                 // split line
1056                 for (col_type c = col(cur.idx()) + 1; c < ncols(); ++c)
1057                         swap(cell(index(r, c)), cell(index(r + 1, c)));
1058
1059                 // split cell
1060                 splitCell(cur);
1061                 swap(cell(cur.idx()), cell(cur.idx() + ncols() - 1));
1062                 if (cur.idx() > 0)
1063                         --cur.idx();
1064                 cur.pos() = cur.lastpos();
1065
1066                 //mathcursor->normalize();
1067                 //cmd = FuncRequest(LFUN_FINISHED_LEFT);
1068                 break;
1069         }
1070
1071         case LFUN_TABULAR_FEATURE: {
1072                 recordUndo(cur);
1073                 //lyxerr << "handling tabular-feature " << cmd.argument << endl;
1074                 istringstream is(cmd.argument);
1075                 string s;
1076                 is >> s;
1077                 if (s == "valign-top")
1078                         valign('t');
1079                 else if (s == "valign-middle")
1080                         valign('c');
1081                 else if (s == "valign-bottom")
1082                         valign('b');
1083                 else if (s == "align-left")
1084                         halign('l', col(cur.idx()));
1085                 else if (s == "align-right")
1086                         halign('r', col(cur.idx()));
1087                 else if (s == "align-center")
1088                         halign('c', col(cur.idx()));
1089                 else if (s == "append-row")
1090                         for (int i = 0, n = extractInt(is); i < n; ++i)
1091                                 addRow(cur.row());
1092                 else if (s == "delete-row")
1093                         for (int i = 0, n = extractInt(is); i < n; ++i) {
1094                                 delRow(cur.row());
1095                                 if (cur.idx() > nargs())
1096                                         cur.idx() -= ncols();
1097                         }
1098                 else if (s == "copy-row")
1099                         for (int i = 0, n = extractInt(is); i < n; ++i)
1100                                 copyRow(cur.row());
1101                 else if (s == "swap-row")
1102                         swapRow(cur.row());
1103                 else if (s == "append-column")
1104                         for (int i = 0, n = extractInt(is); i < n; ++i) {
1105                                 row_type r = cur.row();
1106                                 col_type c = col(cur.idx());
1107                                 addCol(c);
1108                                 cur.idx() = index(r, c);
1109                         }
1110                 else if (s == "delete-column")
1111                         for (int i = 0, n = extractInt(is); i < n; ++i) {
1112                                 row_type r = cur.row();
1113                                 col_type c = col(cur.idx());
1114                                 delCol(col(cur.idx()));
1115                                 cur.idx() = index(r, c);
1116                                 if (cur.idx() > nargs())
1117                                         cur.idx() -= ncols();
1118                         }
1119                 else if (s == "copy-column")
1120                         copyCol(col(cur.idx()));
1121                 else if (s == "swap-column")
1122                         swapCol(col(cur.idx()));
1123                 else {
1124                         cur.undispatched();
1125                         break;
1126                 }
1127                 lyxerr << "returning FINISHED_LEFT" << endl;
1128                 break;
1129         }
1130
1131         case LFUN_PASTE: {
1132                 recordUndo(cur);
1133                 lyxerr << "MathGridInset: PASTE: " << cmd << std::endl;
1134                 istringstream is(cmd.argument);
1135                 int n = 0;
1136                 is >> n;
1137                 MathGridInset grid(1, 1);
1138                 mathed_parse_normal(grid, lyx::cap::getSelection(cur.buffer(), n));
1139                 if (grid.nargs() == 1) {
1140                         // single cell/part of cell
1141                         cur.cell().insert(cur.pos(), grid.cell(0));
1142                         cur.pos() += grid.cell(0).size();
1143                 } else {
1144                         // multiple cells
1145                         col_type const numcols =
1146                                 min(grid.ncols(), ncols() - col(cur.idx()));
1147                         row_type const numrows =
1148                                 min(grid.nrows(), nrows() - cur.row());
1149                         for (row_type r = 0; r < numrows; ++r) {
1150                                 for (col_type c = 0; c < numcols; ++c) {
1151                                         idx_type i = index(r + cur.row(), c + col(cur.idx()));
1152                                         cell(i).insert(0, grid.cell(grid.index(r, c)));
1153                                 }
1154                                 // append the left over horizontal cells to the last column
1155                                 idx_type i = index(r + cur.row(), ncols() - 1);
1156                                 for (MathInset::col_type c = numcols; c < grid.ncols(); ++c)
1157                                         cell(i).append(grid.cell(grid.index(r, c)));
1158                         }
1159                         // append the left over vertical cells to the last _cell_
1160                         idx_type i = nargs() - 1;
1161                         for (row_type r = numrows; r < grid.nrows(); ++r)
1162                                 for (col_type c = 0; c < grid.ncols(); ++c)
1163                                         cell(i).append(grid.cell(grid.index(r, c)));
1164                 }
1165                 break;
1166         }
1167
1168         case LFUN_HOMESEL:
1169         case LFUN_HOME:
1170         case LFUN_WORDLEFTSEL:
1171         case LFUN_WORDLEFT:
1172                 cur.selHandle(cmd.action == LFUN_WORDLEFTSEL || cmd.action == LFUN_HOMESEL);
1173                 cur.macroModeClose();
1174                 if (cur.pos() != 0) {
1175                         cur.pos() = 0;
1176                 } else if (cur.idx() % cur.ncols() != 0) {
1177                         cur.idx() -= cur.idx() % cur.ncols();
1178                         cur.pos() = 0;
1179                 } else if (cur.idx() != 0) {
1180                         cur.idx() = 0;
1181                         cur.pos() = 0;
1182                 } else {
1183                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
1184                 }
1185                 break;
1186
1187         case LFUN_WORDRIGHTSEL:
1188         case LFUN_WORDRIGHT:
1189         case LFUN_ENDSEL:
1190         case LFUN_END:
1191                 cur.selHandle(cmd.action == LFUN_WORDRIGHTSEL || cmd.action == LFUN_ENDSEL);
1192                 cur.macroModeClose();
1193                 cur.clearTargetX();
1194                 if (cur.pos() != cur.lastpos()) {
1195                         cur.pos() = cur.lastpos();
1196                 } else if ((cur.idx() + 1) % cur.ncols() != 0) {
1197                         cur.idx() += cur.ncols() - 1 - cur.idx() % cur.ncols();
1198                         cur.pos() = cur.lastpos();
1199                 } else if (cur.idx() != cur.lastidx()) {
1200                         cur.idx() = cur.lastidx();
1201                         cur.pos() = cur.lastpos();
1202                 } else {
1203                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
1204                 }
1205                 break;
1206
1207         default:
1208                 MathNestInset::priv_dispatch(cur, cmd);
1209         }
1210 }
1211
1212
1213 bool MathGridInset::getStatus(LCursor & cur, FuncRequest const & cmd,
1214                 FuncStatus & flag) const
1215 {
1216         bool ret = true;
1217         switch (cmd.action) {
1218         case LFUN_TABULAR_FEATURE:
1219 #if 0
1220                 // should be more precise
1221                 if (v_align_ == '\0') {
1222                         flag.enable(true);
1223                         break;
1224                 }
1225                 if (cmd.argument.empty()) {
1226                         flag.enable(false);
1227                         break;
1228                 }
1229                 if (!contains("tcb", cmd.argument[0])) {
1230                         flag.enable(false);
1231                         break;
1232                 }
1233                 flag.setOnOff(cmd.argument[0] == v_align_);
1234 #endif
1235                 flag.enabled(true);
1236                 break;
1237         default:
1238                 ret = MathNestInset::getStatus(cur, cmd, flag);
1239                 break;
1240         }
1241         return ret;
1242 }