]> git.lyx.org Git - lyx.git/blob - src/mathed/MathMacro.cpp
Put MathData on a diet: transfer dimension cache to BufferView' CoordCache along...
[lyx.git] / src / mathed / MathMacro.cpp
1 /**
2  * \file MathMacro.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Alejandro Aguilar Sierra
7  * \author André Pönitz
8  *
9  * Full author contact details are available in file CREDITS.
10  */
11
12 #include <config.h>
13
14 #include "MathMacro.h"
15 #include "MathSupport.h"
16 #include "MathExtern.h"
17 #include "MathStream.h"
18
19 #include "Buffer.h"
20 #include "Cursor.h"
21 #include "debug.h"
22 #include "BufferView.h"
23 #include "LaTeXFeatures.h"
24 #include "frontends/Painter.h"
25
26
27 namespace lyx {
28
29 using std::string;
30 using std::max;
31
32
33 /// This class is the value of a macro argument, technically
34 /// a wrapper of the cells of MathMacro.
35 class MathMacroArgumentValue : public InsetMath {
36 public:
37         ///
38         MathMacroArgumentValue(MathMacro const & mathMacro, size_t idx)
39                 : mathMacro_(mathMacro), idx_(idx) {}
40         ///
41         void metrics(MetricsInfo & mi, Dimension & dim) const;
42         ///
43         void draw(PainterInfo &, int x, int y) const;
44         ///
45         int kerning() const { return mathMacro_.cell(idx_).kerning(); }
46
47 private:
48         Inset * clone() const;
49         MathMacro const & mathMacro_;
50         size_t idx_;
51 };
52
53
54 Inset * MathMacroArgumentValue::clone() const
55 {
56         return new MathMacroArgumentValue(*this);
57 }
58
59
60 void MathMacroArgumentValue::metrics(MetricsInfo & mi, Dimension & dim) const
61 {
62         // unlock outer macro in arguments, and lock it again later
63         MacroData const & macro = MacroTable::globalMacros().get(mathMacro_.name());
64         macro.unlock();
65         mathMacro_.cell(idx_).metrics(mi, dim);
66         macro.lock();
67 }
68
69
70 void MathMacroArgumentValue::draw(PainterInfo & pi, int x, int y) const
71 {
72         // unlock outer macro in arguments, and lock it again later
73         MacroData const & macro = MacroTable::globalMacros().get(mathMacro_.name());
74         macro.unlock();
75         mathMacro_.cell(idx_).draw(pi, x, y);
76         macro.lock();
77 }
78
79
80 MathMacro::MathMacro(docstring const & name, int numargs)
81         : InsetMathNest(numargs), name_(name), editing_(false)
82 {}
83
84
85 Inset * MathMacro::clone() const
86 {
87         MathMacro * x = new MathMacro(*this);
88         x->expanded_ = MathData();
89         x->macroBackup_ = MacroData();
90         return x;
91 }
92
93
94 docstring MathMacro::name() const
95 {
96         return name_;
97 }
98
99
100 void MathMacro::cursorPos(BufferView const & bv,
101                 CursorSlice const & sl, bool boundary, int & x, int & y) const
102 {
103         // We may have 0 arguments, but InsetMathNest requires at least one.
104         if (nargs() > 0)
105                 InsetMathNest::cursorPos(bv, sl, boundary, x, y);
106 }
107
108
109 void MathMacro::metrics(MetricsInfo & mi, Dimension & dim) const
110 {
111         kerning_ = 0;
112         if (!MacroTable::globalMacros().has(name())) {
113                 mathed_string_dim(mi.base.font, "Unknown: " + name(), dim);
114         } else {
115                 MacroData const & macro = MacroTable::globalMacros().get(name());
116
117                 if (macroBackup_ != macro)
118                         updateExpansion();
119
120                 if (macro.locked()) {
121                         mathed_string_dim(mi.base.font, "Self reference: " + name(), dim);
122                 } else if (editing(mi.base.bv)) {
123                         Font font = mi.base.font;
124                         augmentFont(font, from_ascii("lyxtex"));
125                         tmpl_.metrics(mi, dim);
126                         // FIXME UNICODE
127                         dim.wid += mathed_string_width(font, name()) + 10;
128                         // FIXME UNICODE
129                         int ww = mathed_string_width(font, from_ascii("#1: "));
130                         for (idx_type i = 0; i < nargs(); ++i) {
131                                 MathData const & c = cell(i);
132                                 Dimension dimc;
133                                 c.metrics(mi, dimc);
134                                 dim.wid  = max(dim.wid, dimc.width() + ww);
135                                 dim.des += dimc.height() + 10;
136                         }
137                         editing_ = true;
138                 } else {
139                         macro.lock();
140                         expanded_.metrics(mi, dim);
141                         macro.unlock();
142                         kerning_ = expanded_.kerning();
143                         editing_ = false;
144                 }
145         }
146         // Cache the inset dimension. 
147         setDimCache(mi, dim);
148 }
149
150
151 void MathMacro::draw(PainterInfo & pi, int x, int y) const
152 {
153         if (!MacroTable::globalMacros().has(name())) {
154                 // FIXME UNICODE
155                 drawStrRed(pi, x, y, "Unknown: " + name());
156         } else {
157                 MacroData const & macro = MacroTable::globalMacros().get(name());
158
159                 // warm up cache
160                 for (size_t i = 0; i < nargs(); ++i)
161                         cell(i).setXY(*pi.base.bv, x, y);
162
163                 if (macro.locked()) {
164                         // FIXME UNICODE
165                         drawStrRed(pi, x, y, "Self reference: " + name());
166                 } else if (editing_) {
167                         Font font = pi.base.font;
168                         augmentFont(font, from_ascii("lyxtex"));
169                         Dimension const dim = dimension(*pi.base.bv);
170                         Dimension const & dim_tmpl = tmpl_.dimension(*pi.base.bv);
171                         int h = y - dim.ascent() + 2 + dim_tmpl.ascent();
172                         pi.pain.text(x + 3, h, name(), font);
173                         int const w = mathed_string_width(font, name());
174                         tmpl_.draw(pi, x + w + 12, h);
175                         h += dim_tmpl.descent();
176                         Dimension ldim;
177                         docstring t = from_ascii("#1: ");
178                         mathed_string_dim(font, t, ldim);
179                         for (idx_type i = 0; i < nargs(); ++i) {
180                                 MathData const & c = cell(i);
181                                 Dimension const & dimc = c.dimension(*pi.base.bv);
182                                 h += max(dimc.ascent(), ldim.asc) + 5;
183                                 c.draw(pi, x + ldim.wid, h);
184                                 char_type str[] = { '#', '1', ':', '\0' };
185                                 str[1] += static_cast<char_type>(i);
186                                 pi.pain.text(x + 3, h, str, font);
187                                 h += max(dimc.descent(), ldim.des) + 5;
188                         }
189                 } else {
190                         macro.lock();
191                         expanded_.draw(pi, x, y);
192                         macro.unlock();
193                 }
194
195                 // edit mode changed?
196                 if (editing_ != editing(pi.base.bv) || macroBackup_ != macro)
197                         pi.base.bv->cursor().updateFlags(Update::Force);
198         }
199 }
200
201
202 void MathMacro::drawSelection(PainterInfo & pi, int x, int y) const
203 {
204         // We may have 0 arguments, but InsetMathNest requires at least one.
205         if (nargs() > 0)
206                 InsetMathNest::drawSelection(pi, x, y);
207 }
208
209
210 void MathMacro::validate(LaTeXFeatures & features) const
211 {
212         string const require = MacroTable::globalMacros().get(name()).requires();
213         if (!require.empty())
214                 features.require(require);
215
216         if (name() == "binom" || name() == "mathcircumflex")
217                 features.require(to_utf8(name()));
218 }
219
220
221 Inset * MathMacro::editXY(Cursor & cur, int x, int y)
222 {
223         // We may have 0 arguments, but InsetMathNest requires at least one.
224         if (nargs() > 0) {
225                 // Prevent crash due to cold coordcache
226                 // FIXME: This is only a workaround, the call of
227                 // InsetMathNest::editXY is correct. The correct fix would
228                 // ensure that the coordcache of the arguments is valid.
229                 if (!editing(&cur.bv())) {
230                         edit(cur, true);
231                         return this;
232                 }
233                 return InsetMathNest::editXY(cur, x, y);
234         }
235         return this;
236 }
237
238
239 bool MathMacro::idxFirst(Cursor & cur) const
240 {
241         cur.updateFlags(Update::Force);
242         return InsetMathNest::idxFirst(cur);
243 }
244
245
246 bool MathMacro::idxLast(Cursor & cur) const
247 {
248         cur.updateFlags(Update::Force);
249         return InsetMathNest::idxLast(cur);
250 }
251
252
253 bool MathMacro::idxUpDown(Cursor & cur, bool up) const
254 {
255         if (up) {
256                 if (cur.idx() == 0)
257                         return false;
258                 --cur.idx();
259         } else {
260                 if (cur.idx() + 1 >= nargs())
261                         return false;
262                 ++cur.idx();
263         }
264         cur.pos() = cell(cur.idx()).x2pos(cur.x_target());
265         return true;
266 }
267
268
269 bool MathMacro::notifyCursorLeaves(Cursor & cur)
270 {
271         cur.updateFlags(Update::Force);
272         return InsetMathNest::notifyCursorLeaves(cur);
273 }
274
275
276 void MathMacro::maple(MapleStream & os) const
277 {
278         updateExpansion();
279         lyx::maple(expanded_, os);
280 }
281
282
283 void MathMacro::mathmlize(MathStream & os) const
284 {
285         updateExpansion();
286         lyx::mathmlize(expanded_, os);
287 }
288
289
290 void MathMacro::octave(OctaveStream & os) const
291 {
292         updateExpansion();
293         lyx::octave(expanded_, os);
294 }
295
296
297 void MathMacro::updateExpansion() const
298 {
299         MacroData const & macro = MacroTable::globalMacros().get(name());
300
301         // create MathMacroArgumentValue object pointing to the cells of the macro
302         std::vector<MathData> values(nargs());
303         for (size_t i = 0; i != nargs(); ++i)
304                                 values[i].insert(0, MathAtom(new MathMacroArgumentValue(*this, i)));
305         macro.expand(values, expanded_);
306         asArray(macro.def(), tmpl_);
307         macroBackup_ = macro;
308 }
309
310
311 void MathMacro::infoize(odocstream & os) const
312 {
313         os << "Macro: " << name();
314 }
315
316
317 void MathMacro::infoize2(odocstream & os) const
318 {
319         os << "Macro: " << name();
320
321 }
322
323
324 } // namespace lyx