]> git.lyx.org Git - lyx.git/blob - src/mathed/math_gridinset.C
whichFont down to 5.3%
[lyx.git] / src / mathed / math_gridinset.C
1 #ifdef __GNUG__
2 #pragma implementation
3 #endif
4
5 #include "math_gridinset.h"
6 #include "math_mathmlstream.h"
7 #include "math_streamstr.h"
8 #include "lyxfont.h"
9 #include "Painter.h"
10 #include "debug.h"
11
12
13 namespace {
14
15 string verboseHLine(int n)
16 {
17         string res;
18         for (int i = 0; i < n; ++i)
19                 res += "\\hline";
20         return res + ' ';
21 }
22
23 }
24
25
26 ////////////////////////////////////////////////////////////// 
27
28
29 MathGridInset::RowInfo::RowInfo()
30         : lines_(0), skip_(0)
31 {}
32
33
34
35 int MathGridInset::RowInfo::skipPixels() const
36 {
37 #ifdef WITH_WARNINGS
38 #warning fix this once the interface to LyXLength has improved
39 #endif
40         return int(crskip_.value());
41 }
42
43
44
45 ////////////////////////////////////////////////////////////// 
46
47
48 MathGridInset::ColInfo::ColInfo()
49         : align_('c'), leftline_(false), rightline_(false), lines_(0)
50 {}
51
52
53 ////////////////////////////////////////////////////////////// 
54
55
56 MathGridInset::MathGridInset(char v, string const & h)
57         : MathNestInset(guessColumns(h)), rowinfo_(2), colinfo_(guessColumns(h) + 1)
58 {
59         setDefaults();
60         valign(v);
61         halign(h);
62 }
63
64
65 MathGridInset::MathGridInset(col_type m, row_type n)
66         : MathNestInset(m * n), rowinfo_(n + 1), colinfo_(m + 1), v_align_('c')
67 {
68         setDefaults();
69 }
70
71
72 MathGridInset::MathGridInset(col_type m, row_type n, char v, string const & h)
73         : MathNestInset(m * n), rowinfo_(n + 1), colinfo_(m + 1), v_align_(v)
74 {
75         setDefaults();
76         valign(v);
77         halign(h);
78 }
79
80
81 MathInset * MathGridInset::clone() const
82 {
83         return new MathGridInset(*this);
84 }
85
86
87 MathInset::idx_type MathGridInset::index(row_type row, col_type col) const
88 {
89         return col + ncols() * row;
90 }
91
92
93 void MathGridInset::setDefaults()
94 {
95         if (ncols() <= 0)
96                 lyxerr << "positive number of columns expected\n";
97         if (nrows() <= 0)
98                 lyxerr << "positive number of rows expected\n";
99         for (col_type col = 0; col < ncols(); ++col) {
100                 colinfo_[col].align_ = defaultColAlign(col);
101                 colinfo_[col].skip_  = defaultColSpace(col);
102         }
103 }
104
105
106 void MathGridInset::halign(string const & hh)
107 {
108         col_type col = 0;
109         for (string::const_iterator it = hh.begin(); it != hh.end(); ++it) {
110                 char c = *it;
111                 if (c == '|') {
112                         colinfo_[col].lines_++;
113                 } else if (c == 'c' || c == 'l' || c == 'r') {
114                         colinfo_[col].align_ = c;
115                         ++col;
116                         colinfo_[col].lines_ = 0;
117                 } else {
118                         lyxerr << "unkown column separator: '" << c << "'\n";
119                 }
120         }
121                         
122 /*
123         col_type n = hh.size();
124         if (n > ncols())
125                 n = ncols();
126         for (col_type col = 0; col < n; ++col)
127                 colinfo_[col].align_ = hh[col];
128 */
129 }
130
131
132 MathGridInset::col_type MathGridInset::guessColumns(string const & hh) const
133 {
134         col_type col = 0;
135         for (string::const_iterator it = hh.begin(); it != hh.end(); ++it)
136                 if (*it == 'c' || *it == 'l' || *it == 'r')
137                         ++col;
138         return col;
139 }
140
141
142 void MathGridInset::halign(char h, col_type col)
143 {
144         colinfo_[col].align_ = h;
145 }
146
147
148 char MathGridInset::halign(col_type col) const
149 {
150         return colinfo_[col].align_;
151 }
152
153
154 string MathGridInset::halign() const
155 {
156         string res;
157         for (col_type col = 0; col < ncols(); ++col) {
158                 res += string(colinfo_[col].lines_, '|');
159                 res += colinfo_[col].align_;
160         } 
161         return res + string(colinfo_[ncols()].lines_, '|');
162 }
163
164
165 void MathGridInset::valign(char c)
166 {
167         v_align_ = c;
168 }
169
170
171 char MathGridInset::valign() const
172 {
173         return v_align_;
174 }
175
176
177 MathGridInset::col_type MathGridInset::ncols() const
178 {
179         return colinfo_.size() - 1;
180 }
181
182
183 MathGridInset::row_type MathGridInset::nrows() const
184 {
185         return rowinfo_.size() - 1;
186 }
187
188
189 MathGridInset::col_type MathGridInset::col(idx_type idx) const
190 {
191         return idx % ncols();
192 }
193
194
195 MathGridInset::row_type MathGridInset::row(idx_type idx) const
196 {
197         return idx / ncols();
198 }
199
200
201 void MathGridInset::vcrskip(LyXLength const & crskip, row_type row)
202 {
203         rowinfo_[row].crskip_ = crskip;
204 }
205
206
207 LyXLength MathGridInset::vcrskip(row_type row) const
208 {
209         return rowinfo_[row].crskip_;
210 }
211
212
213 void MathGridInset::metrics(MathMetricsInfo const & mi) const
214 {
215         // let the cells adjust themselves
216         MathNestInset::metrics(mi);
217
218         // compute absolute sizes of vertical structure
219         for (row_type row = 0; row < nrows(); ++row) {
220                 int asc  = 0;
221                 int desc = 0;
222                 for (col_type col = 0; col < ncols(); ++col) {
223                         MathXArray const & c = xcell(index(row, col));
224                         asc  = std::max(asc,  c.ascent());
225                         desc = std::max(desc, c.descent());
226                 }
227                 rowinfo_[row].ascent_  = asc;
228                 rowinfo_[row].descent_ = desc;
229         }
230         rowinfo_[0].ascent_       += hlinesep() * rowinfo_[0].lines_;
231         rowinfo_[nrows()].ascent_  = 0;
232         rowinfo_[nrows()].descent_ = 0;
233
234         // compute vertical offsets
235         rowinfo_[0].offset_ = 0;
236         for (row_type row = 1; row <= nrows(); ++row) {
237                 rowinfo_[row].offset_  =        
238                         rowinfo_[row - 1].offset_  +
239                         rowinfo_[row - 1].descent_ +
240                         rowinfo_[row - 1].skipPixels() +
241                         rowsep() +
242                         rowinfo_[row].lines_ * hlinesep() +
243                         rowinfo_[row].ascent_;
244         }
245
246         // adjust vertical offset
247         int h = 0;
248         switch (v_align_) {
249                 case 't':
250                         h = 0;
251                         break;
252                 case 'b':
253                         h = rowinfo_[nrows() - 1].offset_;
254                         break;
255                 default:
256                         h = rowinfo_[nrows() - 1].offset_ / 2;
257         }
258         for (row_type row = 0; row <= nrows(); ++row)
259                 rowinfo_[row].offset_ -= h;
260         
261
262         // compute absolute sizes of horizontal structure
263         for (col_type col = 0; col < ncols(); ++col) {
264                 int wid = 0;
265                 for (row_type row = 0; row < nrows(); ++row) 
266                         wid = std::max(wid, xcell(index(row, col)).width());
267                 colinfo_[col].width_ = wid;
268         }
269         colinfo_[ncols()].width_  = 0;
270
271         // compute horizontal offsets
272         colinfo_[0].offset_ = border();
273         for (col_type col = 1; col <= ncols(); ++col) {
274                 colinfo_[col].offset_ =
275                         colinfo_[col - 1].offset_ +
276                         colinfo_[col - 1].width_ + 
277                         colinfo_[col - 1].skip_ +
278                         colsep() + 
279                         colinfo_[col].lines_ * vlinesep();
280         }
281
282
283         width_   =   colinfo_[ncols() - 1].offset_      
284                        + colinfo_[ncols() - 1].width_
285                  + vlinesep() * colinfo_[ncols()].lines_
286                        + border();
287
288         ascent_  = - rowinfo_[0].offset_          
289                        + rowinfo_[0].ascent_
290                  + hlinesep() * rowinfo_[0].lines_
291                        + border();
292
293         descent_ =   rowinfo_[nrows() - 1].offset_
294                        + rowinfo_[nrows() - 1].descent_
295                  + hlinesep() * rowinfo_[nrows()].lines_
296                        + border();
297
298
299 /*      
300         // Increase ws_[i] for 'R' columns (except the first one)
301         for (int i = 1; i < nc_; ++i)
302                 if (align_[i] == 'R')
303                         ws_[i] += 10 * df_width;
304         // Increase ws_[i] for 'C' column
305         if (align_[0] == 'C')
306                 if (ws_[0] < 7 * workwidth / 8)
307                         ws_[0] = 7 * workwidth / 8;
308         
309         // Adjust local tabs
310         width = colsep();
311         for (cxrow = row_.begin(); cxrow; ++cxrow) {   
312                 int rg = COLSEP;
313                 int lf = 0;
314                 for (int i = 0; i < nc_; ++i) {
315                         bool isvoid = false;
316                         if (cxrow->getTab(i) <= 0) {
317                                 cxrow->setTab(i, df_width);
318                                 isvoid = true;
319                         }
320                         switch (align_[i]) {
321                         case 'l':
322                                 lf = 0;
323                                 break;
324                         case 'c':
325                                 lf = (ws_[i] - cxrow->getTab(i))/2; 
326                                 break;
327                         case 'r':
328                         case 'R':
329                                 lf = ws_[i] - cxrow->getTab(i);
330                                 break;
331                         case 'C':
332                                 if (cxrow == row_.begin())
333                                         lf = 0;
334                                 else if (cxrow.is_last())
335                                         lf = ws_[i] - cxrow->getTab(i);
336                                 else
337                                         lf = (ws_[i] - cxrow->getTab(i))/2; 
338                                 break;
339                         }
340                         int const ww = (isvoid) ? lf : lf + cxrow->getTab(i);
341                         cxrow->setTab(i, lf + rg);
342                         rg = ws_[i] - ww + colsep();
343                         if (cxrow == row_.begin())
344                                 width += ws_[i] + colsep();
345                 }
346                 cxrow->setBaseline(cxrow->getBaseline() - ascent);
347         }
348 */
349 }
350
351
352 void MathGridInset::draw(Painter & pain, int x, int y) const
353 {
354         for (idx_type idx = 0; idx < nargs(); ++idx)
355                 xcell(idx).draw(pain, x + cellXOffset(idx), y + cellYOffset(idx));
356
357         for (row_type row = 0; row <= nrows(); ++row)
358                 for (int i = 0; i < rowinfo_[row].lines_; ++i) {
359                         int yy = y + rowinfo_[row].offset_ - rowinfo_[row].ascent_
360                                 - i * hlinesep() - hlinesep()/2 - rowsep()/2;
361                         pain.line(x + 1, yy, x + width_ - 1, yy);
362                 }
363
364         for (col_type col = 0; col <= ncols(); ++col)
365                 for (int i = 0; i < colinfo_[col].lines_; ++i) {
366                         int xx = x + colinfo_[col].offset_
367                                 - i * vlinesep() - vlinesep()/2 - colsep()/2;
368                         pain.line(xx, y - ascent_ + 1, xx, y + descent_ - 1);
369                 }
370 }
371
372
373 string MathGridInset::eolString(row_type row) const
374 {
375         string eol;
376
377         if (!rowinfo_[row].crskip_.zero())
378                 eol += "[" + rowinfo_[row].crskip_.asLatexString() + "]";
379
380         // make sure an upcoming '[' does not break anything
381         if (row + 1 < nrows()) {
382                 MathArray const & c = cell(index(row + 1, 0));
383                 if (c.size() && c.front()->getChar() == '[')
384                         //eol += "[0pt]";
385                         eol += "{}";
386         }
387
388         // only add \\ if necessary
389         if (eol.empty() && row + 1 == nrows())
390                 return string();
391
392         return "\\\\" + eol + '\n';
393 }
394
395
396 string MathGridInset::eocString(col_type col) const
397 {
398         if (col + 1 == ncols())
399                 return string();
400         return " & ";
401 }
402
403
404 void MathGridInset::addRow(row_type row)
405 {
406         rowinfo_.insert(rowinfo_.begin() + row + 1, RowInfo());
407         cells_.insert(cells_.begin() + (row + 1) * ncols(), ncols(), MathXArray());
408 }
409
410
411 void MathGridInset::appendRow()
412 {
413         rowinfo_.push_back(RowInfo());
414         //cells_.insert(cells_.end(), ncols(), MathXArray());
415         for (col_type col = 0; col < ncols(); ++col)
416                 cells_.push_back(cells_type::value_type());
417 }
418
419
420 void MathGridInset::delRow(row_type row)
421 {
422         if (nrows() == 1)
423                 return;
424
425         cells_type::iterator it = cells_.begin() + row * ncols(); 
426         cells_.erase(it, it + ncols());
427
428         rowinfo_.erase(rowinfo_.begin() + row);
429 }
430
431
432 void MathGridInset::addCol(col_type newcol)
433 {
434         const col_type nc = ncols();
435         const row_type nr = nrows();
436         cells_type new_cells((nc + 1) * nr);
437         
438         for (row_type row = 0; row < nr; ++row)
439                 for (col_type col = 0; col < nc; ++col)
440                         new_cells[row * (nc + 1) + col + (col > newcol)]
441                                 = cells_[row * nc + col];
442         std::swap(cells_, new_cells);
443
444         ColInfo inf;
445         inf.skip_  = defaultColSpace(newcol);
446         inf.align_ = defaultColAlign(newcol);
447         colinfo_.insert(colinfo_.begin() + newcol, inf);
448 }
449
450
451 void MathGridInset::delCol(col_type col)
452 {
453         if (ncols() == 1)
454                 return;
455
456         cells_type tmpcells;
457         for (col_type i = 0; i < nargs(); ++i) 
458                 if (i % ncols() != col)
459                         tmpcells.push_back(cells_[i]);
460         std::swap(cells_, tmpcells);
461
462         colinfo_.erase(colinfo_.begin() + col);
463 }
464
465
466 int MathGridInset::cellXOffset(idx_type idx) const
467 {
468         col_type c = col(idx);
469         int x = colinfo_[c].offset_;
470         char align = colinfo_[c].align_;
471         if (align == 'r' || align == 'R')
472                 x += colinfo_[c].width_ - xcell(idx).width(); 
473         if (align == 'c' || align == 'C')
474                 x += (colinfo_[c].width_ - xcell(idx).width()) / 2; 
475         return x;
476 }
477
478
479 int MathGridInset::cellYOffset(idx_type idx) const
480 {
481         return rowinfo_[row(idx)].offset_;
482 }
483
484
485 bool MathGridInset::idxUp(idx_type & idx) const
486 {
487         if (idx < ncols())
488                 return false;
489         idx -= ncols();
490         return true;
491 }
492
493         
494 bool MathGridInset::idxDown(idx_type & idx) const
495 {
496         if (idx >= ncols() * (nrows() - 1))
497                 return false;
498         idx += ncols();
499         return true;
500 }
501         
502         
503 bool MathGridInset::idxLeft(idx_type & idx, pos_type & pos) const
504 {
505         // leave matrix if on the left hand edge
506         if (col(idx) == 0)
507                 return false;
508         --idx;
509         pos = cell(idx).size();
510         return true;
511 }
512         
513         
514 bool MathGridInset::idxRight(idx_type & idx, pos_type & pos) const
515 {
516         // leave matrix if on the right hand edge
517         if (col(idx) + 1 == ncols())
518                 return false;
519         ++idx;
520         pos = 0;
521         return true;
522 }
523
524
525 bool MathGridInset::idxFirst(idx_type & idx, pos_type & pos) const
526 {
527         switch (v_align_) {
528                 case 't':
529                         idx = 0;
530                         break;
531                 case 'b':
532                         idx = (nrows() - 1) * ncols();
533                         break;
534                 default: 
535                         idx = ((nrows() - 1) / 2) * ncols();
536         }
537         pos = 0;
538         return true;
539 }
540
541
542 bool MathGridInset::idxLast(idx_type & idx, pos_type & pos) const
543 {
544         switch (v_align_) {
545                 case 't':
546                         idx = ncols() - 1;
547                         break;
548                 case 'b':
549                         idx = nargs() - 1;
550                         break;
551                 default:
552                         idx = ((nrows() - 1) / 2 + 1) * ncols() - 1;
553         }
554         pos = cell(idx).size();
555         return true;
556 }
557
558
559 bool MathGridInset::idxHome(idx_type & idx, pos_type & pos) const
560 {
561         if (pos > 0) {
562                 pos = 0;
563                 return true;
564         }
565         if (col(idx) > 0) {
566                 idx -= idx % ncols();
567                 pos = 0;
568                 return true;
569         }
570         if (idx > 0) {
571                 idx = 0;
572                 pos = 0;
573                 return true;
574         }
575         return false;
576 }
577
578
579 bool MathGridInset::idxEnd(idx_type & idx, pos_type & pos) const
580 {
581         if (pos < cell(idx).size()) {
582                 pos = cell(idx).size();
583                 return true;
584         }
585         if (col(idx) < ncols() - 1) {
586                 idx = idx - idx % ncols() + ncols() - 1;
587                 pos = cell(idx).size();
588                 return true;
589         }
590         if (idx < nargs() - 1) {
591                 idx = nargs() - 1;
592                 pos = cell(idx).size();
593                 return true;
594         }
595         return false;
596 }
597
598
599 void MathGridInset::idxDelete(idx_type & idx, bool & popit, bool & deleteit)
600 {
601         popit    = false;
602         deleteit = false;
603
604         // nothing to do if we are in the last row of the inset
605         if (row(idx) + 1 == nrows())
606                 return;
607
608         // try to delete entire sequence of ncols() empty cells if possible
609         for (idx_type i = idx; i < idx + ncols(); ++i)
610                 if (cell(i).size())
611                         return;
612
613         // move cells if necessary
614         for (idx_type i = index(row(idx), 0); i < idx; ++i)
615                 cell(i).swap(cell(i + ncols()));
616                 
617         delRow(row(idx));
618
619         if (idx >= nargs())
620                 idx = nargs() - 1;
621
622         // undo effect of Ctrl-Tab (i.e. pull next cell)
623         //if (idx + 1 != nargs()) 
624         //      cell(idx).swap(cell(idx + 1));
625 }
626
627
628 MathGridInset::RowInfo const & MathGridInset::rowinfo(row_type row) const
629 {
630         return rowinfo_[row];
631 }
632
633
634 MathGridInset::RowInfo & MathGridInset::rowinfo(row_type row)
635 {
636         return rowinfo_[row];
637 }
638
639
640 std::vector<MathInset::idx_type>
641         MathGridInset::idxBetween(idx_type from, idx_type to) const
642 {
643         row_type r1 = std::min(row(from), row(to));
644         row_type r2 = std::max(row(from), row(to));
645         col_type c1 = std::min(col(from), col(to));
646         col_type c2 = std::max(col(from), col(to));
647         std::vector<idx_type> res;
648         for (row_type i = r1; i <= r2; ++i)
649                 for (col_type j = c1; j <= c2; ++j)
650                         res.push_back(index(i, j));
651         return res;
652 }
653
654
655
656 void MathGridInset::normalize(NormalStream & os) const
657 {
658         os << "[grid ";
659         for (row_type row = 0; row < nrows(); ++row) {
660                 os << "[row ";
661                 for (col_type col = 0; col < ncols(); ++col)
662                         os << "[cell " << cell(index(row, col)) << ']';
663                 os << ']';
664         }
665         os << ']';
666 }
667
668
669 void MathGridInset::mathmlize(MathMLStream & os) const
670 {
671         os << MTag("mtable");
672         for (row_type row = 0; row < nrows(); ++row) {
673                 os << MTag("mtr");
674                 for (col_type col = 0; col < ncols(); ++col) 
675                         os << cell(index(row, col));
676                 os << ETag("mtr");
677         }
678         os << ETag("mtable");
679 }
680
681
682 void MathGridInset::write(WriteStream & os) const
683 {
684         for (row_type row = 0; row < nrows(); ++row) {
685                 os << verboseHLine(rowinfo_[row].lines_);
686                 for (col_type col = 0; col < ncols(); ++col) 
687                         os << cell(index(row, col)) << eocString(col);
688                 os << eolString(row);
689         }
690         string const s = verboseHLine(rowinfo_[nrows()].lines_);
691         if (!s.empty() && s != " ")
692                 os << "\\\\" << s;
693 }
694
695
696 int MathGridInset::colsep() const
697 {
698         return 6;
699 }
700
701
702 int MathGridInset::rowsep() const
703 {
704         return 6;
705 }
706
707
708 int MathGridInset::hlinesep() const
709 {
710         return 3;
711 }
712
713
714 int MathGridInset::vlinesep() const
715 {
716         return 3;
717 }
718
719
720 int MathGridInset::border() const
721 {
722         return 2;
723 }