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