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