]> git.lyx.org Git - lyx.git/blob - src/mathed/math_gridinset.C
some visual support for \lefteqn
[lyx.git] / src / mathed / math_gridinset.C
1 #ifdef __GNUG__
2 #pragma implementation
3 #endif
4
5 #include "math_gridinset.h"
6 #include "support/LOstream.h"
7 #include "debug.h"
8
9
10 namespace {
11
12 ///
13 int const MATH_COLSEP = 10;
14 ///
15 int const MATH_ROWSEP = 10;
16 ///
17 int const MATH_BORDER = 2;
18
19 }
20
21
22 ////////////////////////////////////////////////////////////// 
23
24
25 MathGridInset::RowInfo::RowInfo()
26         : upperline_(false), lowerline_(false)
27 {}
28
29
30
31 int MathGridInset::RowInfo::skipPixels() const
32 {
33 #ifdef WITH_WARNINGS
34 #warning fix this once the interface to LyXLength has improved
35 #endif
36         return int(skip_.value());
37 }
38
39
40
41 ////////////////////////////////////////////////////////////// 
42
43
44 MathGridInset::ColInfo::ColInfo()
45         : align_('c'), leftline_(false), rightline_(false), skip_(MATH_COLSEP)
46 {}
47
48
49 ////////////////////////////////////////////////////////////// 
50
51
52 MathGridInset::MathGridInset(col_type m, row_type n)
53         : MathNestInset(m * n), rowinfo_(n), colinfo_(m), v_align_('c')
54 {
55         setDefaults();
56 }
57
58
59 MathGridInset::MathGridInset(int m, int n, char v, string const & h)
60         : MathNestInset(m * n), rowinfo_(n), colinfo_(m), v_align_(v)
61 {
62         setDefaults();
63         valign(v);
64         halign(h);
65 }
66
67
68 MathInset::idx_type MathGridInset::index(row_type row, col_type col) const
69 {
70         return col + ncols() * row;
71 }
72
73
74 void MathGridInset::setDefaults()
75 {
76         if (ncols() <= 0)
77                 lyxerr << "positve number of columns expected\n";
78         if (nrows() <= 0)
79                 lyxerr << "positve number of rows expected\n";
80         for (col_type col = 0; col < ncols(); ++col) {
81                 colinfo_[col].align_ = defaultColAlign(col);
82                 colinfo_[col].skip_  = defaultColSpace(col);
83         }
84 }
85
86
87 void MathGridInset::halign(string const & hh)
88 {
89         col_type n = hh.size();
90         if (n > ncols())
91                 n = ncols();
92         for (col_type col = 0; col < n; ++col)
93                 colinfo_[col].align_ = hh[col];
94 }
95
96
97 void MathGridInset::halign(char h, col_type col)
98 {
99         colinfo_[col].align_ = h;
100 }
101
102
103 char MathGridInset::halign(col_type col) const
104 {
105         return colinfo_[col].align_;
106 }
107
108
109
110 void MathGridInset::valign(char c)
111 {
112         v_align_ = c;
113 }
114
115
116 char MathGridInset::valign() const
117 {
118         return v_align_;
119 }
120
121
122
123 void MathGridInset::vskip(LyXLength const & skip, row_type row)
124 {
125         rowinfo_[row].skip_ = skip;
126 }
127
128
129 LyXLength MathGridInset::vskip(row_type row) const
130 {
131         return rowinfo_[row].skip_;
132 }
133
134
135 void MathGridInset::metrics(MathStyles st) const
136 {
137         // let the cells adjust themselves
138         MathNestInset::metrics(st);
139         size_ = st;
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         xo(x);
261         yo(y);
262         for (idx_type idx = 0; idx < nargs(); ++idx)
263                 xcell(idx).draw(pain, x + cellXOffset(idx), y + cellYOffset(idx));
264 }
265
266
267 void MathGridInset::write(std::ostream & os, bool fragile) const
268 {
269         for (row_type row = 0; row < nrows(); ++row) {
270                 for (col_type col = 0; col < ncols(); ++col) {
271                         cell(index(row, col)).write(os, fragile);
272                         os << eocString(col);
273                 }
274                 os << eolString(row);
275         }
276 }
277
278
279 void MathGridInset::writeNormal(std::ostream & os) const
280 {
281         os << "[grid ";
282         for (row_type row = 0; row < nrows(); ++row) {
283                 os << "[row ";
284                 for (col_type col = 0; col < ncols(); ++col) {
285                         os << "[cell ";
286                         cell(index(row, col)).writeNormal(os);
287                         os << "]";
288                 }
289                 os << "]";
290         }
291         os << "]";
292 }
293
294
295 string MathGridInset::eolString(row_type row) const
296 {
297         if (row + 1 == nrows()) 
298                 return "";
299
300         if (rowinfo_[row].skip_.value() != 0)
301                 return "\\\\[" + rowinfo_[row].skip_.asLatexString() + "]\n";
302
303         // make sure an upcoming '[' does not break anything
304         MathArray const & c = cell(index(row + 1, 0));
305         if (c.size() && (*c.begin())->getChar() == '[')
306                 return "\\\\[0pt]\n";
307
308         return "\\\\\n";
309 }
310
311
312 string MathGridInset::eocString(col_type col) const
313 {
314         if (col + 1 == ncols())
315                 return "";
316         return " & ";
317 }
318
319
320 void MathGridInset::addRow(row_type row)
321 {
322         rowinfo_.insert(rowinfo_.begin() + row + 1, RowInfo());
323         cells_.insert(cells_.begin() + (row + 1) * ncols(), ncols(), MathXArray());
324 }
325
326
327 void MathGridInset::appendRow()
328 {
329         rowinfo_.push_back(RowInfo());
330         for (col_type col = 0; col < ncols(); ++col)
331                 cells_.push_back(cells_type::value_type());
332 }
333
334
335 void MathGridInset::delRow(row_type row)
336 {
337         if (nrows() == 1)
338                 return;
339
340         cells_type::iterator it = cells_.begin() + row * ncols(); 
341         cells_.erase(it, it + ncols());
342
343         rowinfo_.erase(rowinfo_.begin() + row);
344 }
345
346
347 void MathGridInset::addCol(col_type newcol)
348 {
349         const col_type nc = ncols();
350         const row_type nr = nrows();
351         cells_type new_cells((nc + 1) * nr);
352         
353         for (row_type row = 0; row < nr; ++row)
354                 for (col_type col = 0; col < nc; ++col)
355                         new_cells[row * (nc + 1) + col + (col > newcol)]
356                                 = cells_[row * nc + col];
357         std::swap(cells_, new_cells);
358
359         ColInfo inf;
360         inf.skip_  = defaultColSpace(newcol);
361         inf.align_ = defaultColAlign(newcol);
362         colinfo_.insert(colinfo_.begin() + newcol, inf);
363 }
364
365
366 void MathGridInset::delCol(col_type col)
367 {
368         if (ncols() == 1)
369                 return;
370
371         cells_type tmpcells;
372         for (col_type i = 0; i < nargs(); ++i) 
373                 if (i % ncols() != col)
374                         tmpcells.push_back(cells_[i]);
375         std::swap(cells_, tmpcells);
376
377         colinfo_.erase(colinfo_.begin() + col);
378 }
379
380
381 int MathGridInset::cellXOffset(idx_type idx) const
382 {
383         col_type c = col(idx);
384         int x = colinfo_[c].offset_;
385         char align = colinfo_[c].align_;
386         if (align == 'r' || align == 'R')
387                 x += colinfo_[c].width_ - xcell(idx).width(); 
388         if (align == 'c' || align == 'C')
389                 x += (colinfo_[c].width_ - xcell(idx).width()) / 2; 
390         return x;
391 }
392
393
394 int MathGridInset::cellYOffset(idx_type idx) const
395 {
396         return rowinfo_[row(idx)].offset_;
397 }
398
399
400 bool MathGridInset::idxUp(idx_type & idx, pos_type & pos) const
401 {
402         if (idx < ncols())
403                 return false;
404         int x = cellXOffset(idx) + xcell(idx).pos2x(pos);
405         idx -= ncols();
406         pos = xcell(idx).x2pos(x - cellXOffset(idx));
407         return true;
408 }
409
410         
411 bool MathGridInset::idxDown(idx_type & idx, pos_type & pos) const
412 {
413         if (idx >= ncols() * (nrows() - 1))
414                 return false;
415         int x = cellXOffset(idx) + xcell(idx).pos2x(pos);
416         idx += ncols();
417         pos = xcell(idx).x2pos(x - cellXOffset(idx));
418         return true;
419 }
420         
421         
422 bool MathGridInset::idxLeft(idx_type & idx, pos_type & pos) const
423 {
424         // leave matrix if on the left hand edge
425         if (col(idx) == 0)
426                 return false;
427         idx--;
428         pos = cell(idx).size();
429         return true;
430 }
431         
432         
433 bool MathGridInset::idxRight(idx_type & idx, pos_type & pos) const
434 {
435         // leave matrix if on the right hand edge
436         if (col(idx) == ncols() - 1)
437                 return false;
438         idx++;
439         pos = 0;
440         return true;
441 }
442
443
444 bool MathGridInset::idxFirst(idx_type & idx, pos_type & pos) const
445 {
446         switch (v_align_) {
447                 case 't':
448                         idx = 0;
449                         break;
450                 case 'b':
451                         idx = (nrows() - 1) * ncols();
452                         break;
453                 default: 
454                         idx = (nrows() / 2) * ncols();
455         }
456         pos = 0;
457         return true;
458 }
459
460
461 bool MathGridInset::idxLast(idx_type & idx, pos_type & pos) const
462 {
463         switch (v_align_) {
464                 case 't':
465                         idx = ncols() - 1;
466                         break;
467                 case 'b':
468                         idx = nargs() - 1;
469                         break;
470                 default:
471                         idx = (nrows() / 2 + 1) * ncols() - 1;
472         }
473         pos = cell(idx).size();
474         return true;
475 }
476
477
478 void MathGridInset::idxDelete(idx_type & idx, bool & popit, bool & deleteit)
479 {
480         popit    = false;
481         deleteit = false;
482
483         // delete entire sequence of ncols() empty cells if possible
484         if (idx <= index(nrows() - 1, 0))       {
485                 bool deleterow = true;
486                 for (idx_type i = idx; i < idx + ncols(); ++i)
487                         if (cell(i).size()) {
488                                 deleterow = false;
489                                 break;
490                         }
491
492                 if (deleterow) {
493                         // move cells if necessary
494                         for (idx_type i = index(row(idx), 0); i < idx; ++i)
495                                 cell(i).swap(cell(i + ncols()));
496                         
497                         delRow(row(idx));
498
499                         if (idx >= nargs())
500                                 idx = nargs() - 1;
501                         return;
502                 }
503         }
504
505         // undo effect of Ctrl-Tab (i.e. pull next cell)
506         //if (idx != nargs() - 1) 
507         //      cell(idx).swap(cell(idx + 1));
508 }
509
510
511 void MathGridInset::idxDeleteRange(idx_type /*from*/, idx_type /*to*/)
512 {
513 // leave this unimplemented unless someone wants to have it.
514 /*
515         int n = (to - from) / ncols();
516         int r = from / ncols();
517
518         if (n >= 1) {
519                 cells_type::iterator it = cells_.begin() + from;
520                 cells_.erase(it, it + n * ncols());
521                 rowinfo_.erase(rowinfo_.begin() + r, rowinfo_.begin() + r + n);
522         }
523 */
524 }
525
526
527 MathGridInset::RowInfo const & MathGridInset::rowinfo(row_type row) const
528 {
529         return rowinfo_[row];
530 }
531
532
533 MathGridInset::RowInfo & MathGridInset::rowinfo(row_type row)
534 {
535         return rowinfo_[row];
536 }
537
538
539 std::vector<MathInset::idx_type>
540         MathGridInset::idxBetween(idx_type from, idx_type to) const
541 {
542         row_type r1 = std::min(row(from), row(to));
543         row_type r2 = std::max(row(from), row(to));
544         col_type c1 = std::min(col(from), col(to));
545         col_type c2 = std::max(col(from), col(to));
546         std::vector<idx_type> res;
547         for (row_type i = r1; i <= r2; ++i)
548                 for (col_type j = c1; j <= c2; ++j)
549                         res.push_back(index(i, j));
550         return res;
551 }