]> git.lyx.org Git - lyx.git/blob - src/mathed/math_gridinset.C
small stuff
[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 }
462
463
464 void MathGridInset::metrics(MetricsInfo & mi, Dimension & dim) const
465 {
466         metrics(mi);
467         dim = dim_;
468 }
469
470
471 void MathGridInset::draw(PainterInfo & pi, int x, int y) const
472 {
473         for (idx_type idx = 0; idx < nargs(); ++idx)
474                 cell(idx).draw(pi, x + cellXOffset(idx), y + cellYOffset(idx));
475
476         for (row_type row = 0; row <= nrows(); ++row)
477                 for (int i = 0; i < rowinfo_[row].lines_; ++i) {
478                         int yy = y + rowinfo_[row].offset_ - rowinfo_[row].ascent_
479                                 - i * hlinesep() - hlinesep()/2 - rowsep()/2;
480                         pi.pain.line(x + 1, yy,
481                                      x + dim_.width() - 1, yy,
482                                      LColor::foreground);
483                 }
484
485         for (col_type col = 0; col <= ncols(); ++col)
486                 for (int i = 0; i < colinfo_[col].lines_; ++i) {
487                         int xx = x + colinfo_[col].offset_
488                                 - i * vlinesep() - vlinesep()/2 - colsep()/2;
489                         pi.pain.line(xx, y - dim_.ascent() + 1,
490                                      xx, y + dim_.descent() - 1,
491                                      LColor::foreground);
492                 }
493 }
494
495
496 void MathGridInset::metricsT(TextMetricsInfo const & mi, Dimension & dim) const
497 {
498         // let the cells adjust themselves
499         //MathNestInset::metrics(mi);
500         for (idx_type i = 0; i < nargs(); ++i)
501                 cell(i).metricsT(mi, dim);
502
503         // compute absolute sizes of vertical structure
504         for (row_type row = 0; row < nrows(); ++row) {
505                 int asc  = 0;
506                 int desc = 0;
507                 for (col_type col = 0; col < ncols(); ++col) {
508                         MathArray const & c = cell(index(row, col));
509                         asc  = max(asc,  c.ascent());
510                         desc = max(desc, c.descent());
511                 }
512                 rowinfo_[row].ascent_  = asc;
513                 rowinfo_[row].descent_ = desc;
514         }
515         //rowinfo_[0].ascent_       += hlinesep() * rowinfo_[0].lines_;
516         rowinfo_[nrows()].ascent_  = 0;
517         rowinfo_[nrows()].descent_ = 0;
518
519         // compute vertical offsets
520         rowinfo_[0].offset_ = 0;
521         for (row_type row = 1; row <= nrows(); ++row) {
522                 rowinfo_[row].offset_  =
523                         rowinfo_[row - 1].offset_  +
524                         rowinfo_[row - 1].descent_ +
525                         //rowinfo_[row - 1].skipPixels() +
526                         1 + //rowsep() +
527                         //rowinfo_[row].lines_ * hlinesep() +
528                         rowinfo_[row].ascent_;
529         }
530
531         // adjust vertical offset
532         int h = 0;
533         switch (v_align_) {
534                 case 't':
535                         h = 0;
536                         break;
537                 case 'b':
538                         h = rowinfo_[nrows() - 1].offset_;
539                         break;
540                 default:
541                         h = rowinfo_[nrows() - 1].offset_ / 2;
542         }
543         for (row_type row = 0; row <= nrows(); ++row)
544                 rowinfo_[row].offset_ -= h;
545
546
547         // compute absolute sizes of horizontal structure
548         for (col_type col = 0; col < ncols(); ++col) {
549                 int wid = 0;
550                 for (row_type row = 0; row < nrows(); ++row)
551                         wid = max(wid, cell(index(row, col)).width());
552                 colinfo_[col].width_ = wid;
553         }
554         colinfo_[ncols()].width_  = 0;
555
556         // compute horizontal offsets
557         colinfo_[0].offset_ = border();
558         for (col_type col = 1; col <= ncols(); ++col) {
559                 colinfo_[col].offset_ =
560                         colinfo_[col - 1].offset_ +
561                         colinfo_[col - 1].width_ +
562                         colinfo_[col - 1].skip_ +
563                         1 ; //colsep() +
564                         //colinfo_[col].lines_ * vlinesep();
565         }
566
567
568         dim.wid  =  colinfo_[ncols() - 1].offset_
569                        + colinfo_[ncols() - 1].width_
570                  //+ vlinesep() * colinfo_[ncols()].lines_
571                        + 2;
572
573         dim.asc  = -rowinfo_[0].offset_
574                        + rowinfo_[0].ascent_
575                  //+ hlinesep() * rowinfo_[0].lines_
576                        + 1;
577
578         dim.des  =  rowinfo_[nrows() - 1].offset_
579                        + rowinfo_[nrows() - 1].descent_
580                  //+ hlinesep() * rowinfo_[nrows()].lines_
581                        + 1;
582 }
583
584
585 void MathGridInset::drawT(TextPainter & pain, int x, int y) const
586 {
587         for (idx_type idx = 0; idx < nargs(); ++idx)
588                 cell(idx).drawT(pain, x + cellXOffset(idx), y + cellYOffset(idx));
589 }
590
591
592 string MathGridInset::eolString(row_type row, bool fragile) const
593 {
594         string eol;
595
596         if (!rowinfo_[row].crskip_.zero())
597                 eol += '[' + rowinfo_[row].crskip_.asLatexString() + ']';
598
599         // make sure an upcoming '[' does not break anything
600         if (row + 1 < nrows()) {
601                 MathArray const & c = cell(index(row + 1, 0));
602                 if (c.size() && c.front()->getChar() == '[')
603                         //eol += "[0pt]";
604                         eol += "{}";
605         }
606
607         // only add \\ if necessary
608         if (eol.empty() && row + 1 == nrows())
609                 return string();
610
611         return (fragile ? "\\protect\\\\" : "\\\\") + eol;
612 }
613
614
615 string MathGridInset::eocString(col_type col, col_type lastcol) const
616 {
617         if (col + 1 == lastcol)
618                 return string();
619         return " & ";
620 }
621
622
623 void MathGridInset::addRow(row_type row)
624 {
625         rowinfo_.insert(rowinfo_.begin() + row + 1, RowInfo());
626         cells_.insert
627                 (cells_.begin() + (row + 1) * ncols(), ncols(), MathArray());
628         cellinfo_.insert
629                 (cellinfo_.begin() + (row + 1) * ncols(), ncols(), CellInfo());
630 }
631
632
633 void MathGridInset::appendRow()
634 {
635         rowinfo_.push_back(RowInfo());
636         //cells_.insert(cells_.end(), ncols(), MathArray());
637         for (col_type col = 0; col < ncols(); ++col) {
638                 cells_.push_back(cells_type::value_type());
639                 cellinfo_.push_back(CellInfo());
640         }
641 }
642
643
644 void MathGridInset::delRow(row_type row)
645 {
646         if (nrows() == 1)
647                 return;
648
649         cells_type::iterator it = cells_.begin() + row * ncols();
650         cells_.erase(it, it + ncols());
651
652         vector<CellInfo>::iterator jt = cellinfo_.begin() + row * ncols();
653         cellinfo_.erase(jt, jt + ncols());
654
655         rowinfo_.erase(rowinfo_.begin() + row);
656 }
657
658
659 void MathGridInset::copyRow(row_type row)
660 {
661         addRow(row);
662         for (col_type col = 0; col < ncols(); ++col)
663                 cells_[(row + 1) * ncols() + col] = cells_[row * ncols() + col];
664 }
665
666
667 void MathGridInset::swapRow(row_type row)
668 {
669         if (nrows() == 1)
670                 return;
671         if (row + 1 == nrows())
672                 --row;
673         for (col_type col = 0; col < ncols(); ++col)
674                 swap(cells_[row * ncols() + col], cells_[(row + 1) * ncols() + col]);
675 }
676
677
678 void MathGridInset::addCol(col_type newcol)
679 {
680         const col_type nc = ncols();
681         const row_type nr = nrows();
682         cells_type new_cells((nc + 1) * nr);
683         vector<CellInfo> new_cellinfo((nc + 1) * nr);
684
685         for (row_type row = 0; row < nr; ++row)
686                 for (col_type col = 0; col < nc; ++col) {
687                         new_cells[row * (nc + 1) + col + (col > newcol)]
688                                 = cells_[row * nc + col];
689                         new_cellinfo[row * (nc + 1) + col + (col > newcol)]
690                                 = cellinfo_[row * nc + col];
691                 }
692         swap(cells_, new_cells);
693         swap(cellinfo_, new_cellinfo);
694
695         ColInfo inf;
696         inf.skip_  = defaultColSpace(newcol);
697         inf.align_ = defaultColAlign(newcol);
698         colinfo_.insert(colinfo_.begin() + newcol, inf);
699 }
700
701
702 void MathGridInset::delCol(col_type col)
703 {
704         if (ncols() == 1)
705                 return;
706
707         cells_type tmpcells;
708         vector<CellInfo> tmpcellinfo;
709         for (col_type i = 0; i < nargs(); ++i)
710                 if (i % ncols() != col) {
711                         tmpcells.push_back(cells_[i]);
712                         tmpcellinfo.push_back(cellinfo_[i]);
713                 }
714         swap(cells_, tmpcells);
715         swap(cellinfo_, tmpcellinfo);
716
717         colinfo_.erase(colinfo_.begin() + col);
718 }
719
720
721 void MathGridInset::copyCol(col_type col)
722 {
723         addCol(col);
724         for (row_type row = 0; row < nrows(); ++row)
725                 cells_[row * ncols() + col + 1] = cells_[row * ncols() + col];
726 }
727
728
729 void MathGridInset::swapCol(col_type col)
730 {
731         if (ncols() == 1)
732                 return;
733         if (col + 1 == ncols())
734                 --col;
735         for (row_type row = 0; row < nrows(); ++row)
736                 swap(cells_[row * ncols() + col], cells_[row * ncols() + col + 1]);
737 }
738
739
740 int MathGridInset::cellXOffset(idx_type idx) const
741 {
742         col_type c = col(idx);
743         int x = colinfo_[c].offset_;
744         char align = colinfo_[c].align_;
745         if (align == 'r' || align == 'R')
746                 x += colinfo_[c].width_ - cell(idx).width();
747         if (align == 'c' || align == 'C')
748                 x += (colinfo_[c].width_ - cell(idx).width()) / 2;
749         return x;
750 }
751
752
753 int MathGridInset::cellYOffset(idx_type idx) const
754 {
755         return rowinfo_[row(idx)].offset_;
756 }
757
758
759 bool MathGridInset::idxUpDown(LCursor & cur, bool up) const
760 {
761         if (up) {
762                 if (cur.idx() < ncols())
763                         return false;
764                 cur.idx() -= ncols();
765         } else {
766                 if (cur.idx() >= ncols() * (nrows() - 1))
767                         return false;
768                 cur.idx() += ncols();
769         }
770         cur.pos() = cur.cell().x2pos(cur.x_target() - cur.cell().xo());
771         return true;
772 }
773
774
775 bool MathGridInset::idxLeft(LCursor & cur) const
776 {
777         // leave matrix if on the left hand edge
778         if (cur.col() == 0)
779                 return false;
780         --cur.idx();
781         cur.pos() = cur.lastpos();
782         return true;
783 }
784
785
786 bool MathGridInset::idxRight(LCursor & cur) const
787 {
788         // leave matrix if on the right hand edge
789         if (cur.col() + 1 == ncols())
790                 return false;
791         ++cur.idx();
792         cur.pos() = 0;
793         return true;
794 }
795
796
797 bool MathGridInset::idxFirst(LCursor & cur) const
798 {
799         switch (v_align_) {
800                 case 't':
801                         cur.idx() = 0;
802                         break;
803                 case 'b':
804                         cur.idx() = (nrows() - 1) * ncols();
805                         break;
806                 default:
807                         cur.idx() = ((nrows() - 1) / 2) * ncols();
808         }
809         cur.pos() = 0;
810         return true;
811 }
812
813
814 bool MathGridInset::idxLast(LCursor & cur) const
815 {
816         switch (v_align_) {
817                 case 't':
818                         cur.idx() = ncols() - 1;
819                         break;
820                 case 'b':
821                         cur.idx() = nargs() - 1;
822                         break;
823                 default:
824                         cur.idx() = ((nrows() - 1) / 2 + 1) * ncols() - 1;
825         }
826         cur.pos() = cur.lastpos();
827         return true;
828 }
829
830
831 bool MathGridInset::idxDelete(idx_type & idx)
832 {
833         // nothing to do if we have just one row
834         if (nrows() == 1)
835                 return false;
836
837         // nothing to do if we are in the middle of the last row of the inset
838         if (idx + ncols() > nargs())
839                 return false;
840
841         // try to delete entire sequence of ncols() empty cells if possible
842         for (idx_type i = idx; i < idx + ncols(); ++i)
843                 if (cell(i).size())
844                         return false;
845
846         // move cells if necessary
847         for (idx_type i = index(row(idx), 0); i < idx; ++i)
848                 swap(cell(i), cell(i + ncols()));
849
850         delRow(row(idx));
851
852         if (idx >= nargs())
853                 idx = nargs() - 1;
854
855         // undo effect of Ctrl-Tab (i.e. pull next cell)
856         //if (idx + 1 != nargs())
857         //      cell(idx).swap(cell(idx + 1));
858
859         // we handled the event..
860         return true;
861 }
862
863
864 // reimplement old behaviour when pressing Delete in the last position
865 // of a cell
866 void MathGridInset::idxGlue(idx_type idx)
867 {
868         col_type c = col(idx);
869         if (c + 1 == ncols()) {
870                 if (row(idx) + 1 != nrows()) {
871                         for (col_type cc = 0; cc < ncols(); ++cc)
872                                 cell(idx).append(cell(idx + cc + 1));
873                         delRow(row(idx) + 1);
874                 }
875         } else {
876                 cell(idx).append(cell(idx + 1));
877                 for (col_type cc = c + 2; cc < ncols(); ++cc)
878                         cell(idx - c + cc - 1) = cell(idx - c + cc);
879                 cell(idx - c + ncols() - 1).clear();
880         }
881 }
882
883
884 MathGridInset::RowInfo const & MathGridInset::rowinfo(row_type row) const
885 {
886         return rowinfo_[row];
887 }
888
889
890 MathGridInset::RowInfo & MathGridInset::rowinfo(row_type row)
891 {
892         return rowinfo_[row];
893 }
894
895
896 bool MathGridInset::idxBetween(idx_type idx, idx_type from, idx_type to) const
897 {
898         row_type const ri = row(idx);
899         row_type const r1 = min(row(from), row(to));
900         row_type const r2 = max(row(from), row(to));
901         col_type const ci = col(idx);
902         col_type const c1 = min(col(from), col(to));
903         col_type const c2 = max(col(from), col(to));
904         return r1 <= ri && ri <= r2 && c1 <= ci && ci <= c2;
905 }
906
907
908
909 void MathGridInset::normalize(NormalStream & os) const
910 {
911         os << "[grid ";
912         for (row_type row = 0; row < nrows(); ++row) {
913                 os << "[row ";
914                 for (col_type col = 0; col < ncols(); ++col)
915                         os << "[cell " << cell(index(row, col)) << ']';
916                 os << ']';
917         }
918         os << ']';
919 }
920
921
922 void MathGridInset::mathmlize(MathMLStream & os) const
923 {
924         os << MTag("mtable");
925         for (row_type row = 0; row < nrows(); ++row) {
926                 os << MTag("mtr");
927                 for (col_type col = 0; col < ncols(); ++col)
928                         os << cell(index(row, col));
929                 os << ETag("mtr");
930         }
931         os << ETag("mtable");
932 }
933
934
935 void MathGridInset::write(WriteStream & os) const
936 {
937         for (row_type row = 0; row < nrows(); ++row) {
938                 os << verboseHLine(rowinfo_[row].lines_);
939                 // don't write & and empty cells at end of line
940                 col_type lastcol = 0;
941                 bool emptyline = true;
942                 for (col_type col = 0; col < ncols(); ++col)
943                         if (!cell(index(row, col)).empty()) {
944                                 lastcol = col + 1;
945                                 emptyline = false;
946                         }
947                 for (col_type col = 0; col < lastcol; ++col)
948                         os << cell(index(row, col)) << eocString(col, lastcol);
949                 os << eolString(row, os.fragile());
950                 // append newline only if line wasn't completely empty
951                 // and this was not the last line in the grid
952                 if (!emptyline && row + 1 < nrows())
953                         os << "\n";
954         }
955         string const s = verboseHLine(rowinfo_[nrows()].lines_);
956         if (!s.empty() && s != " ") {
957                 if (os.fragile())
958                         os << "\\protect";
959                 os << "\\\\" << s;
960         }
961 }
962
963
964 int MathGridInset::colsep() const
965 {
966         return 6;
967 }
968
969
970 int MathGridInset::rowsep() const
971 {
972         return 6;
973 }
974
975
976 int MathGridInset::hlinesep() const
977 {
978         return 3;
979 }
980
981
982 int MathGridInset::vlinesep() const
983 {
984         return 3;
985 }
986
987
988 int MathGridInset::border() const
989 {
990         return 1;
991 }
992
993
994 void MathGridInset::splitCell(LCursor & cur)
995 {
996         if (cur.idx() == cur.lastidx())
997                 return;
998         MathArray ar = cur.cell();
999         ar.erase(0, cur.pos());
1000         cur.cell().erase(cur.pos(), cur.lastpos());
1001         ++cur.idx();
1002         cur.pos() = 0;
1003         cur.cell().insert(0, ar);
1004 }
1005
1006
1007 void MathGridInset::priv_dispatch(LCursor & cur, FuncRequest & cmd)
1008 {
1009         //lyxerr << "*** MathGridInset: request: " << cmd << endl;
1010         switch (cmd.action) {
1011
1012         case LFUN_MOUSE_RELEASE:
1013                 //if (cmd.button() == mouse_button::button3) {
1014                 //      GridInsetMailer(*this).showDialog();
1015                 //      return DispatchResult(true, true);
1016                 //}
1017                 MathNestInset::priv_dispatch(cur, cmd);
1018                 return;
1019
1020         case LFUN_INSET_DIALOG_UPDATE:
1021                 GridInsetMailer(*this).updateDialog(&cur.bv());
1022                 return;
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(cur.row());
1034                 if (cur.idx() > cur.lastidx())
1035                         cur.idx() = cur.lastidx();
1036                 if (cur.pos() > cur.lastpos())
1037                         cur.pos() = cur.lastpos();
1038                 return;
1039
1040         case LFUN_CELL_SPLIT:
1041                 ////recordUndo(cur, Undo::ATOMIC);
1042                 splitCell(cur);
1043                 return;
1044
1045         case LFUN_BREAKLINE: {
1046                 ////recordUndo(cur, Undo::INSERT);
1047                 row_type const r = cur.row();
1048                 addRow(r);
1049
1050                 // split line
1051                 for (col_type c = col(cur.idx()) + 1; c < ncols(); ++c)
1052                         swap(cell(index(r, c)), cell(index(r + 1, c)));
1053
1054                 // split cell
1055                 splitCell(cur);
1056                 swap(cell(cur.idx()), cell(cur.idx() + ncols() - 1));
1057                 if (cur.idx() > 0)
1058                         --cur.idx();
1059                 cur.idx() = cur.lastpos();
1060
1061                 //mathcursor->normalize();
1062                 cmd = FuncRequest(LFUN_FINISHED_LEFT);
1063                 return;
1064         }
1065
1066         case LFUN_TABULAR_FEATURE: {
1067                 //lyxerr << "handling tabular-feature " << cmd.argument << endl;
1068                 istringstream is(cmd.argument);
1069                 string s;
1070                 is >> s;
1071                 if (s == "valign-top")
1072                         valign('t');
1073                 else if (s == "valign-middle")
1074                         valign('c');
1075                 else if (s == "valign-bottom")
1076                         valign('b');
1077                 else if (s == "align-left")
1078                         halign('l', col(cur.idx()));
1079                 else if (s == "align-right")
1080                         halign('r', col(cur.idx()));
1081                 else if (s == "align-center")
1082                         halign('c', col(cur.idx()));
1083                 else if (s == "append-row")
1084                         for (int i = 0, n = extractInt(is); i < n; ++i)
1085                                 addRow(cur.row());
1086                 else if (s == "delete-row")
1087                         for (int i = 0, n = extractInt(is); i < n; ++i) {
1088                                 delRow(cur.row());
1089                                 if (cur.idx() > nargs())
1090                                         cur.idx() -= ncols();
1091                         }
1092                 else if (s == "copy-row")
1093                         for (int i = 0, n = extractInt(is); i < n; ++i)
1094                                 copyRow(cur.row());
1095                 else if (s == "swap-row")
1096                         swapRow(cur.row());
1097                 else if (s == "append-column")
1098                         for (int i = 0, n = extractInt(is); i < n; ++i) {
1099                                 row_type r = cur.row();
1100                                 col_type c = col(cur.idx());
1101                                 addCol(c);
1102                                 cur.idx() = index(r, c);
1103                         }
1104                 else if (s == "delete-column")
1105                         for (int i = 0, n = extractInt(is); i < n; ++i) {
1106                                 row_type r = cur.row();
1107                                 col_type c = col(cur.idx());
1108                                 delCol(col(cur.idx()));
1109                                 cur.idx() = index(r, c);
1110                                 if (cur.idx() > nargs())
1111                                         cur.idx() -= ncols();
1112                         }
1113                 else if (s == "copy-column")
1114                         copyCol(col(cur.idx()));
1115                 else if (s == "swap-column")
1116                         swapCol(col(cur.idx()));
1117                 else {
1118                         cur.undispatched();
1119                         return;
1120                 }
1121                 lyxerr << "returning FINISHED_LEFT" << endl;
1122                 return;
1123         }
1124
1125         case LFUN_PASTE: {
1126                 //lyxerr << "pasting '" << cmd.argument << "'" << endl;
1127                 MathGridInset grid(1, 1);
1128                 mathed_parse_normal(grid, cmd.argument);
1129                 if (grid.nargs() == 1) {
1130                         // single cell/part of cell
1131                         cur.cell().insert(cur.pos(), grid.cell(0));
1132                         cur.pos() += grid.cell(0).size();
1133                 } else {
1134                         // multiple cells
1135                         col_type const numcols =
1136                                 min(grid.ncols(), ncols() - col(cur.idx()));
1137                         row_type const numrows =
1138                                 min(grid.nrows(), nrows() - cur.row());
1139                         for (row_type r = 0; r < numrows; ++r) {
1140                                 for (col_type c = 0; c < numcols; ++c) {
1141                                         idx_type i = index(r + cur.row(), c + col(cur.idx()));
1142                                         cell(i).insert(0, grid.cell(grid.index(r, c)));
1143                                 }
1144                                 // append the left over horizontal cells to the last column
1145                                 idx_type i = index(r + cur.row(), ncols() - 1);
1146                                 for (MathInset::col_type c = numcols; c < grid.ncols(); ++c)
1147                                         cell(i).append(grid.cell(grid.index(r, c)));
1148                         }
1149                         // append the left over vertical cells to the last _cell_
1150                         idx_type i = nargs() - 1;
1151                         for (row_type r = numrows; r < grid.nrows(); ++r)
1152                                 for (col_type c = 0; c < grid.ncols(); ++c)
1153                                         cell(i).append(grid.cell(grid.index(r, c)));
1154                 }
1155                 return;
1156         }
1157
1158         case LFUN_HOMESEL:
1159         case LFUN_HOME:
1160         case LFUN_WORDLEFTSEL:
1161         case LFUN_WORDLEFT:
1162                 cur.selHandle(cmd.action == LFUN_WORDLEFTSEL || cmd.action == LFUN_HOMESEL);
1163                 cur.macroModeClose();
1164                 if (cur.pos() != 0)
1165                         cur.pos() = 0;
1166                 else if (cur.idx() != 0)
1167                         cur.idx() = 0;
1168                 else
1169                         cmd = FuncRequest(LFUN_FINISHED_LEFT);
1170                 break;
1171
1172         case LFUN_WORDRIGHTSEL:
1173         case LFUN_WORDRIGHT:
1174         case LFUN_ENDSEL:
1175         case LFUN_END:
1176                 cur.selHandle(cmd.action == LFUN_WORDRIGHTSEL || cmd.action == LFUN_ENDSEL);
1177                 cur.macroModeClose();
1178                 cur.clearTargetX();
1179                 if (cur.pos() != cur.lastpos())
1180                         cur.pos() = cur.lastpos();
1181                 else if (cur.idx() != cur.lastidx())
1182                         cur.idx() = cur.lastidx();
1183                 else
1184                         cmd = FuncRequest(LFUN_FINISHED_RIGHT);
1185                 break;
1186
1187         default:
1188                 MathNestInset::priv_dispatch(cur, cmd);
1189         }
1190 }
1191
1192
1193 bool MathGridInset::getStatus(LCursor & cur, FuncRequest const & cmd,
1194                 FuncStatus & flag) const
1195 {
1196         bool ret = true;
1197         switch (cmd.action) {
1198         case LFUN_TABULAR_FEATURE:
1199 #if 0
1200                 // should be more precise
1201                 if (v_align_ == '\0') {
1202                         flag.enable(true);
1203                         break;
1204                 }
1205                 if (cmd.argument.empty()) {
1206                         flag.enable(false);
1207                         break;
1208                 }
1209                 if (!contains("tcb", cmd.argument[0])) {
1210                         flag.enable(false);
1211                         break;
1212                 }
1213                 flag.setOnOff(cmd.argument[0] == v_align_);
1214 #endif
1215                 flag.enabled(true);
1216                 break;
1217         default:
1218                 ret = MathNestInset::getStatus(cur, cmd, flag);
1219                 break;
1220         }
1221         return ret;
1222 }