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