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