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